少年, Abstract Syntax Tree 了解下!
pshu 码农英语课堂
简单任务
pshu 前段时间有一个简单任务。很简单,升级了Cocos Creator游戏框架,有两组JavaScript的API彻底废弃了,如果调用老的 API 直接 Exeception,所以不得不改了。 一组是 node.getPositionX(),node.getPositionY(),使用 node.x 和 node.y 代替。把对象方法的调用变成对象属性的访问。 这里大家可能觉得比较简单,直接写个 sed -ei 's/getPositionX(\s*)/x/g' ,批量的改掉即可(不知道sed同学赶紧去google 下,命令行下的利器之一)。这个可以解决 80%的问题。但是如果代码格式不是很规范的时候,比如 sed 脚本就有点麻烦了。不过这也是小概率的事情,现在 code formating 大家基本是做的,所以基本就避免了这个问题。
再看另外一个组 node.setPositionX()、node.setPositionY() 也变成属性访问的形式。 面对这样的情况 pshu 就有点才疏学浅了。隐隐觉得 sed 可能是可行的,但是具体怎么弄不知道。如果有读者知道的欢迎留言告诉pshu。
要完美的解决这个问题就不能在文本处理这个层面思考问题了。(sed 处理的时候只是简单的文本替换而已)。这个时候我就有祭出我们的大杀器 AST:abstract syntax tree,抽象语法树了。AST 在 wiki 上的解释是 “源代码语法结构的一种抽象表示”。所以 AST 思考问题的层面是“语法”,比原来的文本不知道高到那里去了。 那之所以说是一种抽象的表示是因为这个语法树构建起来之后,他就可以和具体的语法的表现形式(代码的文本的表现)脱离关系了。
废话说了这么多,我们来看看具体一个 AST 是怎么样的,Look!(记得点开看大图)
左边简单的一句 ‘this.node.getPositionX()’, 在语法树的眼里它是如此的复杂。首先他是一个表达式语句(ExpresstionStatement), 它的表达式为一个调用表达式(CallExpresstion); 调用表达的被调函数(callee) 是一个成员表达式,这个成员表达式(MemberExpression)的属性(Property)是一个名为node
的标识符(identifier), 而成员的对象有嵌套了一个成员表达式。
大家看到这里肯定会吐槽:简单的一句代码变成AST表达起来是如此的啰嗦。是的,确实变啰嗦了,但是啰嗦的本质是因为它有了更多的信息,增加了语法的信息,让字符串有了语法上的意义。这些多出来的信息就能帮我们轻松的完成刚才的简单任务。
比如把 this.node.getPositionX()
变成 this.node.x
,就只要把上图的树变成this.node.x
。听到这里很多人马上就发怵了,当年homebrew 的作者 Max Howell都不会反转二叉树,你现在就直接要求我做树的变换!
冷静!冷静!冷静! 其实很简单的。现在JavaScript 生态里面, AST的产生,AST 的修改变换和 从AST 生成代码,都有很方便的库。pshu 给大家介绍下自己常用的库和如何解决AST 变换的问题。
AST 生成: @babel/parser
load 函数就能方便的加载代码文件,生成 AST,so easy。
AST生成代码:@babel/generator
调用 generate 函数传入 ast即可,返回ast 所对应的代码。
AST 变换:@babel/traverse
最复杂的最后介绍,可说好的是变换啊,怎么是 traverse 遍历呢?首先实际代码的 AST 会非常的庞大,直接变换是一个非常复杂的问题;并且我们实际需要变换的节点也是AST中很局部很小的一部分,所以我们通过遍历的方式到达我们需要修改的AST的节点,然后再完成替换工作。细心的读者可能还会问,被替换过的节点的子节点还会被遍历吗?会的 traverse 库把这些问题都提你解决好了。除此之外 traverse 的接口函数还提供了很多方便的工具函数完成节点替换的任务。比如 replaceWithSourceString()
可以直接用一段代码字符串来替代当前遍历到的节点。
好了说了这么多,不如写个例子看看:把 node.getPositionX()
变换成 node.x
。
gist地址:https://gist.github.com/stormslowly/8b0d1b2c230ebe7414afc9896db650b9
代码很简单吧,而且能覆盖各种格式不规范的情况,这就是抽象的力量。还有那个setPositionX 也变成成员表达式的任务作为课后作业给大家了。AST 虽然听起来高深复杂,有了方便的库类也能让大家给自己赋能完成那些复杂的工作。
技术部分的内容就到这里了,那现在进入讲单词的环节了。今天的单词很简单 handy /ˈhæn.di/, 就是词根 hand 加个y,变成形容词词性;意思是 useful or convenient。中文有个非常地道翻译就是”称手“,武侠小说中经常要挑一件”称手“的武器上沙场。今天的例句就是:
Could you please share me your handy tools in daily work? 能把让你日常工作中称手的工具分享给我吗?
欢迎大家留言告诉 pshu,喜欢本内容记得点赞转发!