python在收到代码内容后,首先要启动两个流程,分别为词法解析和语法解析。看过我编译原理课程的同学对这两个流程应该不陌生。词法解析其实就是把代码里面不同的元素分别归类,例如234,1.35,1e3等这类字符串统一用一个标志或数字来表示,通常它们的标志为NUMBER,对应字符串pi, age等这类变量名统一用标志来表示,例如使用NAME,于是整篇代码会一下子浓缩成一系列标志的排列,例如表达式 a = 100 + 10 就变成了 NAME = NUMBER + NUMBER。

接下来就要根据标志之间的排列组合来分析它们所表达的逻辑,这种分析过程往往把标志直接的逻辑联系使用树状结构表达出来,例如表达式"a+1"它对应的树状结构为:

这个过程叫语法解析,想进一步了解编译原理算法的同学可以点击这里

语法解析本质上是通过预定规则解析符号组合所形成的逻辑,例如上面的语法解析树的构建来自于如下语法:

arith_expr : term (('+'|'-') term)*
tem: factor (('*' | '@' | '/'| '%'|'//' factor)*
...

arith_expr 表示由加号或减号连接起来的算术表达式,term表示由*或/连接起来的算术表达式,上面的表达式也称为巴斯特范式,最早使用在fortran语言编译器的设计上,上面的表示式会一直往下解析,直到遇到不能再解析的token为止,没有编译原理经验的同学对这里的描述可能会很困惑,可以查看上面的链接来获取相关知识。

我们看看python语法中有哪些表达式定义和token定义,执行python工程,然后像下图那样输入相应代码:

上图中通过代码分别导入symbol和token然后将他们打印出来。我们可以直接调用Python编译器提供的接口执行代码的语法解析过程:
···

import symbol
import token
import parser
from pprint import pprint

def lex(expression):
symbols = {v: k for k, v in symbol.dict.items() if isinstance(v, int)} #获取所有表达式符号
tokens = {v: k for k, v in token.dict.items() if isinstance(v, int)} #获取所有标识符
lexicon = {**symbols, **tokens}
st = parser.expr(expression) #解析表达式,返回解析过程中遇到的表达式符号对应的数字
st_list = parser.st2list(st) #将表达式符号对应数字替换成字符串

def replace(l: list):r = []for i in l:if isinstance(i, list):r.append(replace(i))else:if i in lexicon:r.append(lexicon[i])else:r.append(i)return r
return replace(st_list)

pprint(lex(‘a + 1’))
···
上面代码运行后输出结果如下:

['eval_input',['testlist',['test',['or_test',['and_test',['not_test',['comparison',['expr',['xor_expr',['and_expr',['shift_expr',['arith_expr',['term',['factor', ['power', ['atom_expr', ['atom', ['NAME', 'a']]]]]],['PLUS', '+'],['term',['factor',['power', ['atom_expr', ['atom', ['NUMBER', '1']]]]]]]]]]]]]]]]],['NEWLINE', ''],['ENDMARKER', '']]

像eval_input, testlist等都对应上下文无关语法表达式中的表达式符号,它属于编译原理的核心内容,编译器根据这些符号的递归关系来构建DFA,也就是有限状态自动机,然后将标识符输入自动机来构建前面的语法解析树。

编译原理的内容总是比较晦涩,没有涉及过这方面内容的同学在读起来肯定会头皮发麻。在编译原理领域有一本经典叫“龙书”,它的地位相当于佛学中的金刚经,如果你没有一定编译原理基础就直接读它的话,我估计你会吐血而亡。为了减少编译原理的晦涩属性,我们看看一个具体例子,这里我们给Python语法添加一个比较操作符~=,也就是约等于,例如1 == 1.01会返回False,但使用 1 ^= 1.01就能返回True。

这部分功能我在windows上反复尝试发现走不通,需要在linux上才可以,我们可以在Linux上下载同样的代码,或者把当前代码路径共享到linux虚拟机里,然后执行如下命令产生makefile文件:

./configure --with-pydebug

然后本地就会生成makefile文件,但是此时我们先不要编译,首先要做的是进入到Grammar路径,打开Grammar文件做如下修改:

comp_op表示比较操作符,它用于比较两个表达式的结果的对应关系,>=, <= , <, >等符号都是比较表达式符号,这里我们增加了一个”约等于“比较符,也就是"~=",完成后在回到根目录执行:

make  regen-all

完成后在Parser/Token.c中的PyToken_TwoChars函数会增加一段代码:

修改这里后编译器就能识别符号“~=”,但是它还不知道遇到这个符号后应该做什么,因此我们需要修改语法部分,进入到Paser目录,打开Python.asdl文件,找到cmpop的定义进行进行如下修改:

这里的目的实际上是给操作符“~="定义一个标志符,编译器在识别到符号”~=“会给它赋予一个数值,然后代码遇到相应数值时就触发相应操作,实际上Eq, NotEq等其实都对应相应的数值,完成修改后再回到根目录执行:

make regen-ast

执行完后进入Include/目录,打开Python-ast.h就可以看到AlE对应的赋值:

接着我们再次进入Python/目录,打开ast.c做如下修改,在第1199行对应ast_for_comp_op函数,这个函数用来告诉编译器如何识别比较操作符,增加如下代码:
这里的逻辑实际上是让编译器遇到符号"~="时将其转换为数值定义,也就是标识符"AlE"对应的数值,后面我们再进行语法解析时,遇到该数值,我们就知道当前代码读取到了符号”~=“,然后我们就可以采取相应动作,到这里我们就可以将代码全部编译一遍,在根目录执行:

make -j2

过一会编译好后,会在本地目录生成python.exe可执行文件,我们启动它,同时必须附带-X oldparser参数,不然修改不起作用:

./python.exe -X oldparser

然后在命令行中输入 1~=2,点击回车,结果如下:

可以看到编译器奔溃了,其原因在于我们并没有告诉编译器遇到操作符"~=“时它应该执行什么逻辑,我们仅仅让它意识到”~=“是一个比较操作符而已,了解编译原理算法的同学会知道,编译器会根据语法定义构建有限状态自动机,然后每读取一个标志符,状态机就会进入下一个状态,现在我们让编译器能够读取标识符AlE,也就是对应”~=",但是我们没有定义这个遇到这个标识符后下一步的走向,所以状态机遇到这个标识符后,没有下一个状态可以跳转,后面我们再处理这个问题,我们可以输入以下代码看看情况:

这里表明语法解析器已经能够识别符号"~=",只不过它不知道识别到这个符号后该怎么办而已,后面我们再添加处理该符号的相应逻辑,有关编译原理算法的更多信息点击这里

内核级python:编译器的词法和语法解析基本原理相关推荐

  1. 内核级python:调试Python编译器源码

    python编译器在执行时,给它指定要执行的源码文件,或者说直接输入源码字符串就可以驱动脚本的执行流程,其基本框架如下: input层是python编译器用于获取源码的输入方式,事实上Python能够 ...

  2. java词法分析_Hive源码系列(七)编译模块之词法、语法解析 (中)

    ​这篇主要举实际案例说明怎么使用antlr工具.利用antlr生成的Lexer.Parser.TreeParser代码,获取asttree.这些都是hive获取asttree的过程,理解了这些,再理解 ...

  3. 淘宝数据库OceanBase SQL编译器部分 源码阅读--解析SQL语法树

    http://blog.csdn.net/qq910894904/article/details/28658421 OceanBase是阿里巴巴集团自主研发的可扩展的关系型数据库,实现了跨行跨表的事务 ...

  4. 怎样在c语言中声明list这一变量,C语言变量赋值语句的语法解析算法实现

    本篇文章是java开发编译器系列课程的文档,有兴趣的朋友可关注网易云课堂的视频内容: 自己动手用java开发编译器 经过一系列的算法摸索后,我们终于要进入 C 语言编译器开发的进程,这一节,我们的目的 ...

  5. 内核级pyhon:编译python编译器和语法修改

    现在一涉及到编程语言几乎就离不开python,甚至这门语言已经成了割韭菜的手段,各种1元学习python的引流课程层出不穷,从这些现象可以体会到python语法设计之成功.它基本上实现了其创作者的初衷 ...

  6. pg/og内核原理-词法语法解析(更新中)

    目录 概念与含义 流程概览 词法语法代码 parsenode.h kwlist.h scan.l gram.y bison的使用 查看状态机 解决规约冲突 plpgsql的词法与语法 概念与含义 一条 ...

  7. 尝试再造python编译器:龙书重制版

    一段时间前,我们用go编写了python的词法解析器.由于近一段时间事情繁多,同时囊中羞涩,因此更多的精力投入到了和"变现"相关的工作,对编译原理,数据库这些极为基础且底层的技术有 ...

  8. python编写游戏加速器_Numba:用CUDA加速的高性能Python编译器

    [IT168 编译]Python是一种高效的动态编程语言,广泛应用于科学.工程和数据分析应用领域.使python如此受欢迎的因素有很多,包括其干净的.表达性的语法和标准的数据结构,综合的"内 ...

  9. python各个解释器的用途-11 个优秀的 Python 编译器和解释器

    Python 是一门对初学者友好的编程语言,是一种多用途的.解释性的和面向对象的高级语言. 它拥有非常小的程序集,非常易于学习.阅读和维护.其解释器可在Windows.Linux 和 Mac OS 等 ...

最新文章

  1. 激活函数sigmoid和激活函数softmax
  2. 缓存模式以及缓存的数据一致性
  3. 如何破解天翼HG260光纤猫【转】
  4. iOS进阶_Socket(Socket简介代码演练)
  5. 用Go语言建立一个简单的区块链part1:基本原型
  6. 安装程序无法继续 因为你的计算机安装了更新的,xp安装不了ie提示“安装了更新的Internet Explorer版本”怎么办...
  7. python命令行模式怎么输入_python获得命令行输入的参数的两种方式
  8. 吴恩达深度学习神经网络基础编程作业Python Basics with Numpy
  9. python接球游戏
  10. RenderTransformOrigin 的作用
  11. 如何安装Pycharm和汉化包(包括安装软件,无广告)
  12. select默认选中
  13. 树莓派安装Ubuntu系统
  14. win10壁纸存储位置_这是Windows 10存储其默认壁纸的位置
  15. js室内地图开发_支付宝小程序室内地图导航开发-支付宝小程序JS加载esmap地图...
  16. ISP—图像调试实习生(第14天)
  17. 频率单位转换 hz cpd cph
  18. android工具类
  19. [QUANTAXIS量化分析]三因素模型
  20. ISP—图像调试实习生(第二天)

热门文章

  1. 上海12家游戏公司面试横向评测
  2. CSS盒子模型/页面布局
  3. NEO锁仓合约使用说明
  4. 计算机论文字号,计算机硕士毕业论文格式(字体+排版要求)
  5. 腾达ac23虚拟服务器怎么设置,腾达(Tenda)AC23 如何设置上网? | 192路由网
  6. linux内核支持我vxlan,Linux内核轻量级隧道
  7. CSS的2D与3D变换
  8. 绿源液冷电动车的秘密武器——风冷控制器
  9. where 与 having 区别
  10. git pull时出现unable to unlink old 一个不该犯下的错误