当前位置: 首页 > 后端技术 > Python

用Python开发Python解释器

时间:2023-03-26 02:00:40 Python

计算机只能理解机器码。归根结底,编程语言只是一串单词,旨在让人类更容易地编写他们想让计算机做的事情。真正的魔法是由编译器和解释器完成的,它们弥合了两者之间的鸿沟。解释器逐行读取代码并将其转换为机器代码。在本文中,我们将设计一个可以执行算术运算的解释器。我们不会重新发明轮子。本文将使用由DavidM.Beazley开发的词法解析器-PLY(PythonLex-Yacc。PLY可以通过以下方式下载:$pipinstallply我们将简要介绍创建解释器所需的基础知识。标记(标记)标记是为解释器提供有意义信息的最小字符单位。令牌包含一对名称和属性值。让我们从创建令牌名称列表开始。这是必要的步骤。令牌=(#数据类型"NUM","FLOAT",#Arithmeticoperations"PLUS","MINUS","MUL","DIV",#parentheses"LPAREN","RPAREN",)词法分析器(Lexer)将语句转换为记号的过程是称为分词或词法分析。执行词法分析的程序是词法分析器。#标记正则表达式t_PLUS=r"+"t_MINUS=r"-"t_MUL=r"*"t_DIV=r"/"t_LPAREN=r"("t_RPAREN=r")"t_POW=r"^"#忽略空格和tabt_ignore="\t"#为每个规则添加操作deft_FLOAT(t):r"""\d+.\d+"""t.value=float(t.value)returntdeft_NUM(t):r"""\d+"""t.value=int(t.value)returnt#未定义规则字符的错误处理deft_error(t):#t.valueherecontainstheunlabeledrestoftheinputprint(f"keywordnotfound:{t.value[0]}\nline{t.lineno}")t.lexer.skip(1)#如果遇到\n,就换行deft_newline(t):r"""\n+"""t.lexer.lineno+=t.value.count("\n")是导入词法分析器,我们会用到:importply.lex因为lext_是一个特殊的前缀,表示规则定义令牌。每个词法规则都是使用正则表达式制作的,与Python中的re模块兼容。正则表达式可以根据规则扫描输入,搜索匹配的符号串。由正则表达式定义的文法称为正则文法。由正则文法定义的语言称为正则语言。定义好规则后,我们将构建词法分析器。data='a=2+(10-8)/1.0'lexer=lex.lex()lexer.input(data)whiletok:=lexer.token():print(tok)要传递输入字符串,我们使用lexer.输入数据)。lexer.token()将返回下一个LexToken实例,最后返回None。根据上面的规则,代码2+(10-8)/1.0的tag会是:紫色字符代表tag的名称,后面是tag的具体内容。\巴科斯-诺尔范式(Backus-NaurForm,BNF)大多数编程语言都可以使用上下文无关文法来编写。它比常规语言更复杂。对于上下文无关文法,我们使用上下文无关文法,它是描述语言中所有可能文法的规则集。BNF是一种定义语法的方法,它描述了编程语言的句法。让我们看一个例子:symbol:alternative1|alternative2...根据产生式,将:的左侧替换为右侧的值之一。右边的值用|分隔(理解为定义为alternative1或alternative2或...等的符号)。对于我们的这个算法解释器,语言规范格式如下:expression:expression'+'expression||表达式“-”表达式|表达式'/'表达式|表达式“*”表达式|表达式'^'表达式|+表达式|-表达式|(表达)|数量|FLOAT输入标记是NUM、FLOAT、+、-、*、/等符号,称为终结符(不能进一步分解或产生其他符号的字符)。一个表达式由一个终结符和一个规则集组成,这样的表达式称为非终结符。解析器(Parser)我们将使用YACC(YetAnotherCompiler编译器)作为解析器生成器。导入模块:将ply.yacc导入为yacc。fromoperatorimport(add,sub,mul,truediv,pow)#我们的解释器支持的运算符列表ops={"+":add,"-":sub,"*":mul,"/":truediv,"^“:pOW,}defp_expression(p):”“”“表达式:表达式|表达式|表达式|表达式divexpressionmul表达|表达|表达pOW表达式“”if(p[2],p[3])==("/",0):#如果除以0,取"INF"(无穷大)作为值p[0]=float("INF")else:p[0]=ops[p[2]](p[1],p[3])defp_expression_uplus_or_expr(p):"""expression:PLUSexpression%precUPLUS|LPARENexpressionRPAREN"""p[0]=p[2]defp_expression_uminus(p):"""expression:MINUSexpression%precUMINUS"""p[0]=-p[2]defp_expression_num(p):"""表达式:NUM|FLOAT"""p[0]=p[1]#语法错误的规则defp_error(p):print(f"Syntaxerrorin{p.value}")在docstring中,我们会添加适当的语法规范p列表中的元素一一对应文法符号,如下:expression:expressionPLUSexpressionp[0]p[1]p[2]p[3]上面用%precUPLUS和%precUMINUS代表自定义操作,%prec是precedence的缩写,symbols中没有UPLUS和UMINUS这样的名词(本文中这两个自定义操作代表一元正号和符号,实际上UPLUS和UMINUS只是名字,你可以随心所欲。之后,我们可以添加基于表达式的规则。YACC允许为每个标记分配优先级。我们可以使用:precedence=(("left","PLUS","MINUS"),("left","MUL","DIV"),("left","POW"),("right","UPLUS","UMINUS"))在优先级语句中,令牌按优先级顺序排列从低到高.PLUS和MINUS有相同的优先级并且是左关联的(操作从左到右执行)。MUL和DIV的优先级高于PLUS,而MINUS也是左关联的。POW也是一样,但是优先级更高。UPLUS和UMINUS是右结合的(运算从右到左执行)。解析输入我们将使用:parser=yacc.yacc()result=parser.parse(data)print(result)完整代码如下:######################################导入模块######################################fromloggingimport(basicConfig,INFO,getLogger)fromoperatorimport(add,sub,mul,truediv,pow)importply.lexasleximportply.yaccasyacc#我们的解释器支持运算符列表ops={"+":add,"-":sub,"*":mul,"/":truediv,"^":pow,}######################################标志设置#######################################tokens=(#数据类型"NUM","FLOAT",#算术运算"PLUS","MINUS","MUL","DIV","POW",#括号"LPAREN","RPAREN",)######################################标记的正则表达式######################################t_PLUS=r"+"t_MINUS=r"-"t_MUL=r"*"t_DIV=r"/"t_LPAREN=r"("t_RPAREN=r")"t_POW=r"^"#忽略空格和制表符t_ignore="\t"#为每个规则添加操作定义t_FLOAT(t):r"""\d+.\d+"""t.value=float(t.value)返回tdeft_NUM(t):r"""\d+"""t.value=int(t.value)returnt#未定义规则字符的错误处理deft_error(t):#t.valueherecontainsunmarkedrestofinputprint(f"keywordnotfound:{t.value[0]}\nline{t.lineno}")t.lexer.skip(1)#如果你看到\n将它设置为一个新行deft_newline(t):r"""\n+"""t.lexer.lineno+=t.value.count("\n")#########################################设置符号优先级#######################################优先级=((“左”,“加”,“减”"),("左","MUL","DIV"),("左","POW"),("右","UPLUS","UMINUS"))########################################编写BNF规则#######################################defp_expression(p):"""表达式:表达式加表达式|表达式减去表达式|表达式DIV表达式|表达式Mul表达式|ExpressionPowExpression""""""p[2])==("/",0):#thentake"INF"(infinity)asthevaluep[0]=float("INF")else:p[0]=ops[p[2]](p[1],p[3])defp_expression_uplus_or_expr(p):"""expression:PLUSexpression%precUPLUS|LPARENexpressionRPAREN"""p[0]=p[2]defp_expression_uminus(p):"""expression:MINUSexpression%precpUMINUS""0]=-p[2]defp_expression_num(p):"""expression:NUM|FLOAT"""p[0]=p[1]#语法错误规则defp_error(p):print(f"语法错误{p.value}")######################################主程序######################################if__name__=="__main__":basicConfig(level=INFO,filename="logs.txt")lexer=lex.lex()parser=yacc.YACC()WhileTrue:Try:Result=Parser.parse(input(">>>"),debug=getlogger())Print(result)ExceptAttributerror:Print("InvalidSyntax").,本文不解释东西很全,但是希望大家对文中涉及到的表面知识有一个很好的了解。我会尽快发布其他关于这个主题的文章。谢谢大家,祝大家有个美好的一天。以上就是本次分享的全部内容。如果你觉得文章还不错,请关注公众号:Python编程学习圈,每日干货分享,发送“J”也能收到大量学习资料。或者上编程学习网,了解更多编程知识技巧.