把你的JS代码翻译成中文书写的代码,并且可执行
发布在芋头的专栏2016年3月4日view:8331ES6BrettBat编译与执行
在文章任何区域双击击即可给文章添加【评注】!浮到评注点上可以查看详情。

不少人应该知道JS里可以用部分unicode字符来命名变量和方法。想想挺好玩的,不但可以用中文来编程,还可以用颜文字和emoji来编程,想想也是醉了。

不过还是不建议在真实项目中这样做,但是这并不代表不可以用嘛。

今天,跟大家介绍一下我写的一个《英文代码翻译器》可以用一句命令,把你的js代码翻译成中文变量命名并且还可以运行的js代码!

虽然这个项目只是开个玩笑,单其中涉及到的几个技术要点,还是值得一说的。本文主要分以下几节:

介绍 | 代码编译 | 翻译 | 全局变量处理 | 重复翻译结果处理 | 小结

介绍

github地址:https://github.com/xinyu198736/hancode

使用方式:

sudo npm install hancode -g;

#定位到某个目录

hancode -s ./code.js

#会在当前目录生成一个 code.汉.js

#code.汉.js 的代码其实就是本库的中文版,测试可以跑通,并且结果一样。

一个编译后的在线地址:http://f2e.souche.com/assets/js/souche.%E6%B1%89.js

本库主代码编译后的结果:

var 丑化JS = require("uglify-js");

var _ = require("underscore");

var 翻译工具 = require("./translate_util");

var 翻译对象 = {};

module.exports = function(源代码, 回调) {
    var AST的代码 = 丑化JS.parse(源代码);
    var 无功节点 = [];
    var 参考节点 = [];
    var 跨语言 = [];
    AST的代码.walk(new 丑化JS.TreeWalker(function(结) {
        if (结 instanceof 丑化JS.AST_SymbolVar) {
            无功节点.push(结);
            跨语言.push(结.name);
        }
    }));
    AST的代码.walk(new 丑化JS.TreeWalker(function(结) {
        if (结 instanceof 丑化JS.AST_SymbolRef) {
            if (跨语言.indexOf(结.name) != -1) {
                参考节点.push(结);
                跨语言.push(结.name);
            }
        }
    }));
    跨语言 = _.uniq(跨语言);
    console.log("翻译中");
    翻译工具(跨语言, function(E, 结果) {
        翻译对象 = 结果;
        使uniq对象(翻译对象);
        无功节点.forEach(function(结) {
            结.name = 翻译对象[结.name] || 结.name;
        });
        参考节点.forEach(function(结) {
            结.name = 翻译对象[结.name] || 结.name;
        });
        回调(null, AST的代码.print_to_string({
            beautify: true
        }));
    });
};

var 使uniq对象 = function(obj) {
    var 扫描值 = [];
    for (var 我 in obj) {
        var 价值 = obj[我];
        if (扫描值.indexOf(价值) != -1) {
            var 重复计数 = 0;
            扫描值.forEach(function(v) {
                if (v == 价值) {
                    重复计数++;
                }
            });
            obj[我] = 价值 + "_" + 重复计数;
        }
        扫描值.push(价值);
    }
};

代码编译

AST这个名词相信很多人都接触过,在做代码压缩的时候,都是先把js转换成AST语法树,然后操作ast语法树,最后再把ast语法树组装回代码,这样做的安全性比较高,比用单纯的正则匹配精确很多,之前我做过一个拆分代码拼接图案的库,用状态机自己写的,中间处理了很多兼容和条件,而且还不能百分百确保正确,而用AST的话其实就简单很多了。

关于编译理论的知识,说起来实在太长,这里就不深入了,主要讲讲我如何利用其替换js中的变量的。

现在比较流行的JS解析器主要是:(https://github.com/mishoo/UglifyJS2)[UglifyJS2]

UglifyJS2的主要功能其实是用来压缩js代码,不过他的压缩过程大概分了三步:parse,transform,generate code;平常我们可能并不需要关心这三步,一个方法就能搞定,不过稍微研究下就会发现,这三步拆分开很有意思,特别是transform这一步,我们可以替换成自己的逻辑,然后做很多改编代码行为的事情。

看我们的实现代码:

var UglifyJS = require("uglify-js");
//解析ast
var ast_code = UglifyJS.parse(source_code); 

//遍历ast,找出所有定义的变量
//为何分成两次,为了排除没有定义而直接使用的全局变量
ast_code.walk(new UglifyJS.TreeWalker(function(node){
    //如果是变量定义的节点
    if(node instanceof UglifyJS.AST_SymbolVar){
        varNodes.push(node);
        for_trans_words.push(node.name);
    }
}));
ast_code.walk(new UglifyJS.TreeWalker(function(node){
    if(node instanceof UglifyJS.AST_SymbolRef){
        if(for_trans_words.indexOf(node.name)!=-1){
            refNodes.push(node);
            for_trans_words.push(node.name);
        }
    }
}));
... 处理替换ast节点中的name

//生成最终代码
ast_code.print_to_string({
    beautify:true
})    

解析和生成代码,UglifyJS都已经帮我们做好了,我们需要做的就是利用 UglifyJS.TreeWalker 来遍历所有的AST节点,判断其类型,然后替换或者转换。

我们这次的需求其实很简单,就是找到某些节点,把他们的name翻译成中文。

具体的实现见github。

翻译

hancode使用百度翻译来翻译英文到中文。事实上百度翻译支持很多语言的互译,使用也很简单。

百度翻译有专门的云服务平台,地址如下:http://api.fanyi.baidu.com/api/trans/product/index

前端乱炖的url seo也是用的这个api。

全局变量处理

这个库里面有一些小细节,例如有些变量是全局变量,但是AST里并没有标记出来,需要自己判断,如果判断有Ref但是没有定义的变量,即为全局变量,例如 require/console 之类的。

这就是上面的代码片段中为何要遍历两次ast的原因。

重复翻译结果处理

这也是一个细节,如果两个变量的意思很接近,可能会被翻译成同一个结果,如果直接使用会造成代码错乱。

其实一个简单的方法就可以处理:

/**
 * 把一个object里的value都变成唯一的,如果不是唯一的,给他加_1,如果出现两次则_2
 * @param obj
 */
var makeUniqObject = function(obj){
    var scaned_values = []
    for(var i in obj){
        var value = obj[i];
        if(scaned_values.indexOf(value)!=-1){
            var repeat_count = 0;
            scaned_values.forEach(function(v){
                if(v==value){
                    repeat_count++;
                }
            })
            obj[i] = value+"_"+repeat_count
        }
        scaned_values.push(value);
    }
}

结语

这个项目其实没什么高深的,但是的确挺好玩,可以拿去唬人。

评论
发表评论
2年前
赞了此文章!
2年前

想起了传说中的「习语言」…… QQ20160304-0.png

WRITTEN BY
芋头
思考,去做,做好,做的比别人好
TA的新浪微博
PUBLISHED IN

我的收藏