前言
----------------------------------------
上一篇 初探代码编译 - 上篇 简单介绍了编译器的执行过程与原理,
这一篇 来简单的实践一下,考虑到编译器实现的复杂度,这里选择以markdown 文本语言作为对象进行介绍。
正文
-----------------------------------------
1. 什么是 markdown?
定义:
Markdown是一种轻量级标记语言,创始人为约翰·格鲁伯。它允许人们使用易读易写的纯文本格式编写文档,然后转换成有效的XHTML(或者HTML)文档。
由于Markdown的轻量化、易读易写特性,并且对于图片、图表、数学式、代码块都有支持,目前许多网站都广泛使用Markdown来撰写帮助文档或是用于论坛上发表消息。
标准化:
随着时间的推移,Markdown 已经成为典型的转换为HTML的非正式规范和参考实现,出现了许多Markdown实现。人们开发这些主要是由于在基本语法之上需要额外的功能 - 如表格,脚注,定义列表(技术上的HTML描述列表)和HTML块内的Markdown。与此同时,非正式规范中的一些含糊不清引起了人们的注意。这些问题促使Markdown解析器的一些开发人员努力实现标准化。
2016年3月发布了RFC 7763和RFC 7764。RFC 7763 从原始变体引入了MIME类型 text/markdown。RFC 7764讨论并注册了MultiMarkdown、GitHub Flavored Markdown (GFM)、Pandoc、CommonMark及Markdown等变体。
2. markdowm 与 html 的关系
下面列举了 部分 markdown语法 与 html 语法的对应关系
markdown 语法 | html 语法 |
---|---|
# 标题1 | 标题 |
## 标题2 | 标题2 |
空白行分隔 表示段落 | |
行末尾 两个空格 表示换行 | |
_斜体_ | 斜体 |
**粗体** | 粗体 |
`代码` | 代码 |
--- |
|
* 1 * 2 * 3 |
|
由于篇幅有限 ,这里先列举这么多语法,其他更多语法可以参考《了不起的Markdown》。
现在已经知道来 markdown 与 html 的语法对应关系出来,那么应该如何实现一个 compiler 编译器(解析器) 去进行语法识别和转换呢 ?
3. markdowm 词法分析
有了 markdown 的语法标准,就可以进行语法的分析,也就是这里的词法分析,下面以 heading 为例,
首先需要一个 词法解析器,按照 heading 的语法规则 生产出对应的 tokens 序列。
// 词法解析器Class Lexer { constructor(options) { this.tokens = []; this.tokens.links = Object.create(null); this.options = options || {}; this.rules = { // heading 匹配规则 heading: /^ {0,3}(#{1,6}) +([^\n]*?)(?: +#+)? *(?:\n+|$)/ // ... }; } // 生成 token 序列 token(src) { while (src) { // heading if (cap = this.rules.heading.exec(src)) { src = src.substring(cap[0].length); this.tokens.push({ type: 'heading', depth: cap[1].length, text: cap[2] }); continue; } // other // ... } return this.tokens; }}
4. markdown 编译(解析)成 html
上面已经拿到了 markdown 的 tokens 序列,下面还需要把它按照 html 的语法解析成对应的标签, 如下
// 解析器 - Parsing & Compilingclass Parser { constructor(options) { this.tokens = []; this.token = null; this.options = options || {}; this.renderer = new Renderer(); } parse(src) { this.tokens = src.reverse(); // 先进先出 var out = ''; while (this.next()) { out += this.tok(); } return out; } next() { this.token = this.tokens.pop(); return this.token; } tok() { switch (this.token.type) { // ... case 'heading': { return this.renderer.heading( this.token.text, this.token.depth ) } // ... } }}// 渲染器(代码生产器) class Renderer { constructor(options) { this.options = options || {}; } heading(text, level, raw) { if (this.options.headerIds) { return ' + level + ' id="' + this.options.headerPrefix + '">' + text + ' + level + '>\n'; } return ' + level + '>' + text + ' + level + '>\n'; };}
后记
-----------------------------------------
由于 markdown 是一种标记文本语言,因此这里代码解析转换时候 不需要对其进行 抽象语法树的分析(生成/分析 AST 的环节),相当于只进行了词法分析和代码生成的过程。其编译解析过程相对 图灵完备的语言 较简单。
思考题:
根据 javascript 语法 加减乘除 优先级,是否可以构建一个简单的文法解析器呢?
例如:var a = 1 + 3 * 2;
ps:如有不足 欢迎指正
------------------------------
一只前端小菜鸟 | 求知若渴 | 梦想与爱皆不可辜负
-----------------------------
点个关注 点个在看吧