Markdown 是一种轻量级标记语言,它允许人们使用易读易写的纯文本格式编写文档,然后转换成有效的XHTML(或者HTML)文档
项目简介:
这是一个对 markdown文件进行解析,转化为HTML文档的的程序。通过词法分析,构建文档的语法树,然后听过对语法树的深度优先遍历生成html文档。
应用技术:
- 词法分析
- 构建语法树
- 深度优先遍历
在进行词法分析前,先了解一下markdown基础语法,以及与html标签的对应关系
基础语法:
标签对应关系:
下面就开始对MarkDown文档进行分析
通过对语法的了解发现,多数语法标识都是出现在行首,所以我在进行解析时,每次读取一行内容,并对行首进行分析,判断语法内容,有部分语法是可以出现在行内的,所以要做单独处理,这种后面再对它们进行单独处理。
在进行解析时既需要保存语法类型,又需要保存实际内容,如果出现嵌套的语法结构也要妥善保存起来,那么常见的容器无法直接满足需求,所以这里自定义一个Node结构体,来满足数据存储的需求。
定义一个markdown类,类中包含一个Node类型成员,来作为语法树的根节点。所有的基本语法类型都作为他的孩子结点进行保存。
下面就开始正式的语法类型判断,这里有一个细节,要把行首的空格去掉,因为他是和语法无关的,然后判断最简单的语法,即不需要包含内容的,如:空行,分割线。然后再对其他的另行判断。
这里要注意一个点,如果此行内容是在一个代码块结构中,那么不论是空行还是分割线,还是其他类型语法,都不做解析,直接存入代码块语法结点中。
代码块标识奇数次出现和偶数次出现要对代码块标识进行修改。以满足正常功能。
在行首语法中还有一个特殊的地方,无序列表和有序列表在HTML中不仅仅是个单行语法,在使用时,要先创建一个列表(<ul> / <ol>)
然后列表的每一行内容则存储在单独的<li>
中,所以在分析列表的时候也要对奇偶次进行标识,奇数次时,需要先创建一个列表结点,再将行内容以<li>
结点存储,并作为其孩子结点。
如:
在分析完行首语法后,还有一些特殊的语法,他们不一定出现在行首,所以对这部分进行单独处理,前面已经处理完空行,分割线,代码块,标题,无序列表,有序列表,引用节点,剩下的就都归类为普通段落,在插入内容时,再进行语法分析。(部分已经分析出行首语法类型的,插入内容时也要进行这一步,因为可能存在嵌套的语法结构。如:标题是斜体,那么在标题结点中就要嵌套一个斜体结点,在这个结点内才真正存放标题的内容)
由于下面的语法可能出现在行内任意位置,所以在处理的时候要对每一个字符进行分析,所以在下面处理的时候,每次分析一个字符,并进行存储。
如:
void markdown::insert(Node* child, const char* str)
{
//标记是否行内代码
bool incode = false;
//标记是否为粗体内
bool instrong = false;
//标记是否为斜体
bool inem = false;
//标记是否为删除线
bool indline = false;
//创建一个纯文本结点
child->_child.push_back(new Node(nul));
int size = strlen(str);
//一次处理一个字符
for (int i = 0; i < size; i++)
{
//行内代码
if (str[i] == '`')
{
if (incode)
{
//如果当前是行内代码结束位置,创建一个新节点,存后面的内容
child->_child.push_back(new</