javascript编写一个简单的编译器(理解抽象语法树AST)

编译器 是一种接收一段代码,然后把它转成一些其他一种机制。
我们现在来做一个在一张纸上画出一条线,那么我们画出一条线需要定义的条件如下:
使用 Paper定义纸的颜色,Pen定义笔的颜色,Line指画出一条线,100指在颜色参数中代表100%的黑色 或 css中的rgb(0%,0%,0%). 那么生成的线使用灰色来表示,那么就是 50了,纸的面积是 100*100, 线条的宽度是1,线段的起点和终点是相对于左下角的x,y坐标来定义。

Paper 0 (含义是: 定义纸的颜色是白色)
Pen 100 (含义是: 定义笔的颜色是黑色)
Line 0 50 100 50 (含义是:x轴0到100,说明是横向从起点到终点,y轴是50到50,说明是一张纸的中点是一条直线)。

那么编译器是如何工作的?

编译器一般会经过如下几个步骤:
1. 词法分析
2. 语法分析
3. 转换
4. 代码生成

1-1 词法分析(也可以叫做标记)
词法分析将每个关键字(也可以叫标记)使用空格分开. 比如:

Paper 0
Pen 100
Line 0 50 100 50

如上,我们可以把 Paper, Pen,Line 的类型统一可以叫 word, 值就是各个单词了 那么 后面的数字类型我们可以统一叫 number;
比如我们输入 "Paper 0", 那么我们输出的话就变成如下:

[
{type: "word", value: "Paper"},
{type: "number", value: "100"}
]

代码如下:

function lexical (code) {
return code.split(/\s+/)
.filter(function(t) {
return t.length > 0
}).map(function(t) {
console.log(t);
return isNaN(t) ? {type: 'word', value: t} : {type: 'number', value: t}
});
}
var res = lexical("Paper 0");
console.log(res); // [{type: "word", value: "Paper"}, {type: "number", value: '100'}]

打开控制台查看demo输出

1-2 语法分析
语法分析是遍历每个标记,寻找语法信息,并且构建一个叫做AST(抽象语法树)的对象。
下面我们对上面词法分析生成的标记 [{type: "word", value: "Paper"}, {type: "number", value: '100'}] 这样的数据,使用语法分析
构建一个AST(抽象语法树)的对象。代码如下:

function parser(tokens) {
var AST = {
type: 'Drawing',
body: []
};
// 循环依次取出第一个元素,然后删除第一个元素
while (tokens.length > 0) {
var currentItem = tokens.shift();
// 判断类型,如果是单词的话,我们就分析它的语法
if (currentItem.type === 'word') {
switch(currentItem.value) {
case 'Paper' :
var expression = {
type: 'CallExpression',
name: 'Paper',
arguments: []
};
// 继续数组中字段的类型
var nextItem = tokens.shift();
if (nextItem.type === 'number') {
// 在expression对象内部加入参数信息
expression.arguments.push({
type: 'NumberLiteral',
value: nextItem.value
})
// 将expression对象放入我们的AST的body内
AST.body.push(expression);
} else {
throw 'Paper command must be followed by a number.'
}
break;
case 'Pen' :
/* 更多代码 */
break;
case 'Line':
/* 更多代码 */
break;
}
}
}
return AST;
}
var data = [
{ type: 'word', value: 'Paper'},
{ type: 'number', value: 100}
];
var output = parser(data);
console.log(output);
// 打印信息如下
/*
var output = {
'type': 'Drawing',
'body': [{
"type": "CallExpression",
"name": "Paper",
"arguments": [{
"type": "NumberLiteral",
"value": "100"
}]
}]
}
*/

打开控制台查看demo输出

1-3 转换器函数
我们在语法分析上面通过词法分析生成的对象后,在语法分析创建了一个AST(抽象语法树)结构,但是上面的AST结构对我们创建SVG文件没有什么用处,
在SVG中,我们可以使用元素(element)来表示一个Paper。那么转换器函数将AST转换成另一种对SVG友好的AST。代码如下:

function transformer(ast) {
var svg_ast = {
tag: 'svg',
attr: {
width: 100,
height: 100,
viewBox: '0 0 100 100',
xmlns: 'http://www.w3.org/2000/svg',
version: '1.1'
},
body: []
};
// 循环调用ast表达式
while (ast.body.length > 0) {
// 依次取出数组的第一个元素,然后在数组中删除该元素
var node = ast.body.shift();
switch (node.name) {
case 'Paper' :
var paper_color = 100 - node.arguments[0].value;
// 在svg_ast的body内加入rect元素信息
svg_ast.body.push({
tag: 'rest',
attr: {
x: 0,
y: 0,
width: 100,
height: 100,
fill: 'rgb(' + paper_color + '%,' + paper_color + '%,' + paper_color + '%)'
}
})
break; case 'Pen' :
var pen_color = 100 - node.arguments[0].value;
/* 很多代码 */
break; case 'Line' :
/* 很多代码 */
break;
}
}
return svg_ast;
}
var inputElem = {
'type': 'Drawing',
'body': [{
"type": "CallExpression",
"name": "Paper",
"arguments": [{
"type": "NumberLiteral",
"value": "100"
}]
}]
};
var output = transformer(inputElem);
console.log(output);
/*
打印信息如下:
var output = {
"tag": "svg",
"attr": {
"width": 100,
"height": 100,
"viewBox": "0 0 100 100",
"xmlns": "http://www.w3.org/2000/svg",
"version": "1.1"
},
"body": [{
"tag": "rect",
"attr": {
"x": 0,
"y": 0,
"width": 100,
"height": 100,
"fill": "rgb(0%, 0%, 0%)"
}
}]
}
*/

打开控制台查看效果

1-4 生成器函数
作为编译器的最后一步,生成器函数基于上一步产生的新AST来生成SVG代码。
代码如下:

function generator(svg_ast) {
/*
从attr 对象中创建属性字符串
{"width": 100, "height": 100} => 'width="100" height="100"'
*/
function createAttrString(attr) {
return Object.keys(attr).map(function(key) {
return key + '="'+attr[key]+'"'
}).join(' ');
}
// 为svg标签创建属性字符串
var svg_attr = createAttrString(svg_ast.attr); // width="100" height="100" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" version="1.1"
// console.log(svg_attr); // 为每个 svg_ast body中的元素,生成svg标签
var elements = svg_ast.body.map(function(node) {
return '<' + node.tag + ' ' + createAttrString(node.attr) + '></' + node.tag + '>'
}).join('\n\t');
// 使用开和关的svg标签包装来完成svg代码
return '<svg '+ svg_attr +'>\n' + elements + '\n</svg>'
}
var svg_ast = {
"tag": "svg",
"attr": {
"width": 100,
"height": 100,
"viewBox": "0 0 100 100",
"xmlns": "http://www.w3.org/2000/svg",
"version": "1.1"
},
"body": [{
"tag": "rect",
"attr": {
"x": 0,
"y": 0,
"width": 100,
"height": 100,
"fill": "rgb(0%, 0%, 0%)"
}
}]
}
var g = generator(svg_ast);
console.log(g);
/*
打印输出如下:
<svg width="100" height="100" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" version="1.1">
<rect x="0" y="0" width="100" height="100" fill="rgb(0%, 0%, 0%)"></rect>
</svg>
*/

打开控制台查看效果

1-5 把上面的 lexical.js , grammar.js, converter.js 和 generator.js 组装在一起,来作为一个编译器。
我们把这个编译器取个名字叫 svgCompile 编译器吧,我们逐步理解了编译器的步骤,先是创建 词法分析器,然后创建 语法分析器,接着是 转换器,最后就是生成器方法,现在我们需要添加一个 compile(编译)方法来链式调用这四个方法。

 function svgCompile(code) {
return generator(transformer(parser(lexical(code))));
}

下面是compile.html代码如下:

<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="./js/lexical.js"></script>
<script src="./js/parser.js"></script>
<script src="./js/transformer.js"></script>
<script src="./js/generator.js"></script>
<script src="./js/compile.js"></script>
</head>
<body>
<script>
// 调用svgCompile编译器
var code = 'Paper 0 Pen 100 Line 0 50 100 50';
var svg = svgCompile(code);
console.log(svg);
document.body.innerHTML = svg;
/*
打印信息如下:
<svg width="100" height="100" viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" version="1.1">
<rest x="0" y="0" width="100" height="100" fill="rgb(100%,100%,100%)"></rest>
<line x1="0" y1="50" x2="100" y2="50" stroke-linecap="round" stroke="rgb(0%,0%,0%)"></line>
</svg>
*/
</script>
</body>
</html>

下面就是实现使用svg 画一条线的demo.

查看效果:

git上的代码

Tips: 通过网上的资料学习的,关键是想先来理解如何编写一个简单的编译器,及简单的理解AST(抽象语法树)

javascript编写一个简单的编译器(理解抽象语法树AST)的更多相关文章

  1. 理解Babel是如何编译JS代码的及理解抽象语法树(AST)

    Babel是如何编译JS代码的及理解抽象语法树(AST) 1. Babel的作用是?   很多浏览器目前还不支持ES6的代码,但是我们可以通过Babel将ES6的代码转译成ES5代码,让所有的浏览器都 ...

  2. 造轮子系列(三): 一个简单快速的html虚拟语法树(AST)解析器

    前言 虚拟语法树(Abstract Syntax Tree, AST)是解释器/编译器进行语法分析的基础, 也是众多前端编译工具的基础工具, 比如webpack, postcss, less等. 对于 ...

  3. 抽象语法树(AST)

    AST描述 在计算机科学中,抽象语法树(AST)或语法树是用编程语言编写的源代码的抽象语法结构的树表示.树的每个节点表示在源代码中出现的构造.语法是“抽象的”,因为它不代表真实语法中出现的每个细节,而 ...

  4. 使用JavaScript实现一个简单的编译器

    在前端开发中也会或多或少接触到一些与编译相关的内容,常见的有 将ES6.7代码编译成ES5的代码 将SCSS.LESS代码转换成浏览器支持的CSS代码 通过uglifyjs.uglifycss等工具压 ...

  5. 从零写一个编译器(九):语义分析之构造抽象语法树(AST)

    项目的完整代码在 C2j-Compiler 前言 在上一篇完成了符号表的构建,下一步就是输出抽象语法树(Abstract Syntax Tree,AST) 抽象语法树(abstract syntax ...

  6. JavaScript的工作原理:解析、抽象语法树(AST)+ 提升编译速度5个技巧

    这是专门探索 JavaScript 及其所构建的组件的系列文章的第 14 篇. 如果你错过了前面的章节,可以在这里找到它们: JavaScript 是如何工作的:引擎,运行时和调用堆栈的概述! Jav ...

  7. Clang之语法抽象语法树AST

    语法分析器的任务是确定某个单词流是否能够与源语言的语法适配,即设定一个称之为上下文无关语言(context-free language)的语言集合,语法分析器建立一颗与(词法分析出的)输入单词流对应的 ...

  8. 用javascript编写一个简单的随机验证码程序

    简单模拟网页的随机数字验证码,效果图如下: html代码: <div id="content"> <div class="left"> ...

  9. Java抽象语法树AST,JCTree 分析

    JCTree简要分析文章目录JCTree简要分析JCAnnotatedTypeJCAnnotationJCArrayAccessJCArrayTypeTreeJCAssertJCAssignJCAss ...

随机推荐

  1. centos7配置dhcp

    用su 获取root权限 用yum -y install dhcp命令安装dhcp服务(yum是基于RPM包管 理,自动下载RPM包并且安装) 查看安装后生成的配置文件 rpm -qc dhcp 编辑 ...

  2. [3] 微信公众号开发 - 结合UEditor实现图文消息群发功能

    0.写在前面的话 如何实现微信平台后台管理中的,图文消息发送功能? 大概的过程如下: 通过类似表单的形式,将文章各部分内容提交到后台,封装成一个实体类,并持久化到数据库中 需要推送的时候,将不同的文章 ...

  3. 如何使用sourcetree 或 IDEA 自带的git合并代码?

    如何将本地的wyy分支合并并推送到远端的 develop分支? 规则:最好是本地的分支wyy推送到对应的远端origin/wyy ,不建议直接推送到远端不同的分支!!所以 基本思路如下: 1.本地的w ...

  4. websphere部署 hibernate jpa & Error 500: javax/persistence/OneToOne.orphanRemoval()Z

    WebSphere 7 & Javax/Persistence/OneToMany.OrphanRemoval() Error 文章出处:http://www.mkyong.com/websp ...

  5. Spring MVC知识点整理

    网上Spring MVC相关知识点的介绍已经有很多了,但是大部分文章都是介绍其中的一部分知识点. 本文希望能够向读者做一个基本整体的介绍,首先我们先来了解下Spring MVC的基础接口和组件.   ...

  6. 认识StringBuffer类

    概述: StringBuffer类是线程安全的可变字符序列 线程安全效率低 StringBuffer和String的区别 * String是一个不可变的字符序列 * StringBuffer是一个可变 ...

  7. Azure Powershell使用已有Image创建ARM非托管磁盘虚拟机

    生成Image映像文件,记录好Image的URL(下面URL为测试URL,具体请参考实际):ImageURL:https://hlmrgstoragen.blob.core.chinacloudapi ...

  8. JQuery获取图片大小并控制图片文件上传大小以及上图片文件时如何预览图片

    首先我们来看效果图: 点击上传之后如下: 在这里我获取到文件的大小,并且如果超出我设定的大小,则禁止上传! 不多说,上代码:先看div布局: <div class="imageCont ...

  9. 编码格式简介:ASCII码、ANSI、GBK、GB2312、GB18030和Unicode、UTF-8,BOM头

    编码格式简介:ASCII码.ANSI.GBK.GB2312.GB18030和Unicode.UTF-8,BOM头 二进制: 只有0和1. 十进制.十六进制.八进制: 计算机其实挺笨的,它只认识0101 ...

  10. activemq的安装与使用

    一.activemq的安装 环境:CentOS 6.JDK8 1. 确保系统已安装了可用的jdk版本2. 从网上下载 Linux 版的 ActiveMQ( apache-activemq-5.11.1 ...