中文编程是件很有意思的事情,通过基于AST的编译方法,可以让我们实现一种中文编程语言。本期Robust主要聊一聊AST,顺带把基于AST的编译实现过程,以及以中文编程为例子,聊一聊AST的一些用法,让你在工作中,遇到一些特殊语法时,可以快速建立自己的解决方法。
勘误:本期是22期,音频中说成21期。
补充:AST可用于正式线上环境,不仅限于编译器阶段。比如我音频中提到,我用AST完成模板渲染,就是在生产环境使用,否则就没有意义了。
在线收听
网易云音乐:点击播放
喜马拉雅:点击播放
你还可以在苹果自带的 Podcast 应用中搜“Robust”找到我们的节目收听。
捐赠支持
求打赏🙇如果你觉得 Robust 这样一档技术类的谈话节目还不错,希望我继续做下去,不妨打赏支持。
内容大纲
如果一等于一,那么显示“成功”。
人有一双眼睛,我是人。如果我有一双眼睛,那么显示“成功”。
我的名字是“否子戈”。我的学号是141446。显示“我的名字和学号分别是「我的名字」、「我的学号」”。显示我的全部信息。如果我的学号是偶数,那么显示我的名字。
1. 简易编程的共性
- 定义语句:什么是什么,什么属于什么,什么有什么,什么的什么是什么
- 条件语句:如果...那么
- 逻辑语句:不仅...而且,既是……又是,和,或,不是,不会
- 循环语句:?从0到10依次执行,循环5次,一直循环直到我的年龄为12
- 运算语句:1加1,100除50,2的6次方
- 函数语句:我会唱歌:树上的鸟儿成双对。我唱歌。
2. 抽象语法树(Abstract syntax tree)
源代码的语法结构的抽象树状表示。源代码就是一串字符串,该字符串是一种语法结构,既然是结构,那么就可以通过某种形式进行表示,而AST就是其中的一种表示方法,它的特点是将语法结构抽象成一个树状的数据结构,在js中就是一个树状的对象结构。
3. 怎么得到AST
一般分为两步,第一步是通过一个tokenizer方法,遍历原始字符串得到一个一维数组,这个一维数组称为tokens。第二步是通过一个parser方法,遍历tokens得到一个树状结构的对象,这个对象被称为ast。
4. tokenizer方法
本质上,就是如何去切割字符串。首先,我们要定义自己的语法,去寻找语法中的关键字。比如,我们定义「如果一等于一,那么显示“成功”。」这句话的内在逻辑,实际上是通过“如果”“等于”“那么”“显示”以及双引号和句尾的句号表示的。每一个关键字,都有一个自己的类型,由被拆分之后得到的字符串段和每个段自己的类型组成的数组,就是tokens。所以「如果一等于一,那么显示“成功”。」的tokens就是【(“条件”,“如果”),(“数字”,“一”),(“判断”,“等于”),(“数字”,“一”),(“符号”,“,”),(“结果”,“那么”),(“命令”,“显示”),(“文本”,”成功“),(”符号“,”。“)】
具体怎么做呢?随便怎么都可以,只要你得到正确预期的结果就行。
5. parser方法
和tokenizer非常像,也是在遍历过程中,逐渐生成和组合出一个树状的数据结构。在遍历一开始,生成一个根节点,一般该节点的类型叫Program,并用一个body属性存放所有内部节点。根据碰到的token类型,创建一个节点node,或将该token的值作为当前节点的内容之一。总而言之,和tokens单纯的一个数组不同,parser遍历过程中生成的ast是有嵌套结构的,比如一个函数或一个语句块,它本身是一个节点,它的内容是节点的内容,而内容又是由多个平行节点,以及它们的子节点组成。
6. AST转换
得到AST之后,我们就得到了关于一段代码的抽象语法结构。但是,这段抽象语法结构必须要成为可以被计算机识别的代码,才可以在计算机中运行,那具体怎么做呢?我们可以把它转化为其他可以直接运行的编程语言之后,再以其他编程语言直接运行,比如转化为js语言在浏览器中直接运行,或者python在机器上运行,当然,还可以有其他选择,比如直接转化为计算机指令运行,或者转化为c等语言,再编译后得到字节码运行。
怎么才能将上面的AST转化为其他语言运行呢?也分两步:AST转换,代码生成。
AST转换就是将当前的AST转换为另外一个AST。经过转换后,可以方便的生成另外一种编程语言。比如,上面的中文文本中的如果……那么结构,我们可以转化到shell脚本运行,而shell中表示如果……那么的语句是 if...then...fi 语句,所以,我们要将基于中文的这个AST转换为方便生成shell语句的ast。
7. 生成代码
在经过AST转化之后,得到一个新ast,而这个新ast,是比较方便新代码生成的ast。什么样才方便生成新代码呢?生成新代码的过程,实际上也是一个遍历过程,在遍历过程中,不断组合成新代码的字符串。遍历结束的时候,就是新代码字符串完成出现的时候。而由于ast是一个树状结构,遍历树的方法也很简单,所以,拼接字符串而已。
8. 提供修改ast的接口
基于ast的js转化工具babel,提供了插件能力。它的插件主要是对已经解析好的es ast进行调整。同样的道理,我们一般会在这个过程中,提供一种让开发者调整ast的接口。
通过这种调整,开发者可以控制最终输出的代码。比如调整代码输出的顺序,只需要改变ast中节点的顺序即可。
但是你会发现,要通过babel支持新语法则比较麻烦。比如,我们要支持一个@@的语法,非常麻烦,我们需要克隆babel的代码,然后自己去修改babel解析器中的逻辑,才能完成这个语法添加。这是因为,babel并没有直接提供tokenizer和parser的接口。
9. 编译器
这样我们就发明了一门新编程语言,用中文来编程。虽然它的能力还比较弱,只支持简单的加减乘除,但是,你会发现用中文写代码,比英文写代码代码量更少,且表达更准确。
10. AST还能干什么?
我在腾讯内完成了一个从模板到界面的功能,我让客户按照我提供的特定标识进行模板撰写,而这样撰写出来的模板实际上就是有语法的文本。借助ast,这些模板被解析为特定的ast,而利用这个ast,我做了3件事:1)语法高亮,客户写模板时,有颜色提示;2)实时预览,客户写模板时,可以实时看到模板解析为表单后的界面效果;3)生成表单,ast和react结合,直接生成表单。
总而言之,AST可以帮你把一个符合某种语法的字符串,转化为计算机系统中其他任意表现形式的目标。
2020-12-20 2526