You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
import {uniq, extend, flatten, cloneDeep } from "lodash"
// convert to
import uniq from "lodash/uniq";
import extend from "lodash/extend";
import flatten from "lodash/flatten";
import cloneDeep from "lodash/cloneDeep";
抽象语法树
说到抽象语法树就得说到具体语法树,具体差异,这个答案就很棒。
美团的这篇文章对AST的讲解也很棒
我的项目再用他做什么?js代码重构
我们的项目使用的是eggjs, 也用了egg-sequelize,但是有些老旧的代码,游离在外,有100多张表,他们都如下
而我想要的是
Esprima
js的AST的parser有很多,但他们基本都遵循MDN给出的parser API
我这里选择了Esprima,初次使用没有太多考量使用Esprima, 他的语法规范列的很详细, 这篇文章翻译了大部分语法
Esprima api
如上图,我们要是想替换answer这个名字怎么办呢?或者就像是替换为parseInt(2,10), 1: 想美团的那个根据position替换,还有就是直接替换 AST
问题来了我们生成新的AST,那怎么在转换为代码呢?escodegen
到此我们可以发现,我可以找到任何语句,进行任何合法的修改,uglify2还提供了一些便利的方法,比如TreeWalker 遍历语法树,很方便,但还未实操过,有待使用。
利用语法树我们可以做什么?
...还有什么其他功能呢?
---------------------------------------------- 华丽分割线-------------------------------------------
我的好友也有一篇关于js ast的文章里面还介绍了babel插件的写法推荐一下(他可是高质量博主)
在上次写完后我就一直在思考一个问题,如何使用AST来编写DSL,js的表现力来说我觉得和那些有macro的语言还是差很多,不是不能做而是不优雅,比如:
可以看出来,js的元编程其实就是字符串拼接,那么macro 和AST又有什么关系呢?关系就是:macro匹配的参数会转化为AST,然后进行操作。向上面的crystal-lang的define_method的name和content,在宏内部就是AST节点。可以看出来使用macro编写DSL是很方便的。即使像C/C++那样简陋的macro都很有用 就不用说rust,crystal中那么强大的宏了。 js目前不支持macro。
我想写个DSL语法 就叫 '||='
我看了js AST以后就在想esprima可以么?Babel可以么?
答: 目前不可以
原因:这些都只支持js的语法or Next JS的语法比如class, ArrowFuction等等, 你写个 '||=' babel也是识别不了的,肯定会报语法错误(不行你试试,要是真试了的话就别回来了。。。),也就是说你写的babel可以转换的那都是babel支持的语法,也可以说是js的语法或者是即将支持的语法。
问:此时就有人要问了,那JSX呢这可不是js的语法
答:这个是babel/parser内部就支持JSX
扩展: 那是不是就是说只要babel/parser能支持就好了
答:是的
举例:https://github.com/babel/babel/tree/master/packages/babel-parser/src/plugins 在这个文件夹下我们可以看到babel/parser支持的一些js语法之外的一些插件。
问:那接下该怎么做呢?
答:先看两张图
从这两个图我们可以看出 babel的工作原理就是 parse 源文件 到AST 再transform一下到 AST再从AST生成代码
接下来再看一篇关于babel plugin(注意是babel 的plugin不是babel/parser的plugin)的文章从零开始编写一个babel插件
这个文章实现了一个很简单的babel plugin插件干的事儿就是
那么babel plugin和上面的图是什么关系呢? babel plugin就是就是图中AST -> AST的transform过程。也就是说我们想要使用babel plugin,第一步我们写的源文件得能parse到AST。其实我们可以看出来我们使用balbel/plugin能做的也就是 babel支持的语法的替换,删减或者增加。上例就是替换使用一大坨来替换。
回到我们的DSL 'a ||= b' 上来,怎么办?就问怎么办?显然在transform 这个阶段是不行的
问:又问了,那JSX是咋弄的?
答:可以看上面的回答,是babel/parser就支持,也就是说如果我们可以写个babel/parser的plugin就好了
问: 怎么给babel/parser写个plugin呢?
答:你去babel的主库里提pr(233333), 是的目前babel不支持给babel/parser写plugin, 但相信未来不会太遥远的。 #1351 被关闭了,但在很久以前的babel版本中我们是可以的详见adding-custom-syntax-to-babel
问:真的没办法了?
答:babel的parser是从acorn 来的,其实acorn是支持plugin的,炫酷,也就是说只要我们想实现总是可以的。关于他的扩展方式没看到文档有时间在继续吧,acorn。但我们找到了出路,我们也可以随心所欲的写一个新的DSL language了,然后转到JS。
再总结一下,babel plugin的作用域是在(2),他做的是把合法的babel语法AST(都不敢说是js语法了...)转换为合法的JS的AST。 babel/parser plugin的作用域是在(1),他做的是把不合法的源码转换为合法的babel AST。分清babel plugin 和 babel/parser plugin我们就更能理解babel plugin到底是在做啥,他又能做啥。
总归我们是可以用优雅的方式在js中来编写DSL, 但不使用acorn等parser是不行的,其实我们在做前端基础工具时是可以做这些的,采用js的语法,加入合理的DSL 非js 语法使用 acorn转换。(使用decorator和proxy也是可以大大增强js的表现力的)
用大白话来说其实我们就是想要一个其他语言到js的transformer。Typescirpt, PureScirpt, CoffeScript...
The text was updated successfully, but these errors were encountered: