You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何基于C++结合Flex、Bison实现Markdown转HTML自定义编译器?

用C++ + Flex + Bison实现Markdown转HTML编译器的完整流程

1. 先确定核心支持的Markdown子集

先从基础特性入手,避免一开始复杂度太高,建议先实现:

  • 1-6级标题(#至######开头的行)
  • 粗体(**文本**)、斜体(*文本*)
  • 无序列表(- 开头的行)
  • 普通段落文本

2. 搭建项目文件结构

创建以下核心文件:

  • md.l:Flex词法分析器规则定义
  • md.y:Bison语法分析器规则定义
  • codegen.h/codegen.cpp:C++实现的HTML代码生成模块
  • main.cpp:程序入口,负责文件读写与分析器调用

3. 实现Flex词法分析器(md.l)

把Markdown文本拆分为可识别的token,传递给Bison处理:

%{
#include "md.tab.h"
#include <string>
using namespace std;

#define YY_DECL int yylex()
%}

%option noyywrap
%option c++

%%

^#{1,6}[ \t]+.*\n {
    yylval.str = new string(yytext);
    return TITLE;
}

\*\*[^\*]+\*\* {
    yylval.str = new string(yytext);
    return BOLD;
}

\*[^\*]+\* {
    yylval.str = new string(yytext);
    return ITALIC;
}

^-[ \t]+.*\n {
    yylval.str = new string(yytext);
    return LIST_ITEM;
}

[^#\*\-\n]+ {
    yylval.str = new string(yytext);
    return TEXT;
}

\n { return NEWLINE; }

[ \t]+ { /* 忽略空白字符 */ }

%%

4. 实现Bison语法分析器(md.y)

定义语法规则,将token组合为语义单元,触发HTML代码生成动作:

%{
#include <iostream>
#include <string>
#include "codegen.h"
using namespace std;

extern int yylex();
extern void yyerror(const char* s);

CodeGenerator codegen;
%}

%union {
    string* str;
}

%token <str> TITLE BOLD ITALIC LIST_ITEM TEXT
%token NEWLINE

%%

markdown_file: /* 空 */
             | markdown_file element
             ;

element: title_element
       | list_element
       | paragraph_element
       | inline_formatting
       ;

title_element: TITLE NEWLINE {
    string& title_str = *$1;
    int level = 0;
    while (level < title_str.size() && title_str[level] == '#') level++;
    string content = title_str.substr(level + 1);
    content = content.substr(0, content.find_last_not_of("\n") + 1);
    codegen.generateTitle(level, content);
    delete $1;
}

list_element: list_item+ {
    codegen.endList();
}

list_item: LIST_ITEM NEWLINE {
    string& item_str = *$1;
    string content = item_str.substr(item_str.find_first_not_of("- \t"));
    content = content.substr(0, content.find_last_not_of("\n") + 1);
    codegen.generateListItem(content);
    delete $1;
}

paragraph_element: TEXT NEWLINE {
    string& para_str = *$1;
    para_str = para_str.substr(0, para_str.find_last_not_of("\n") + 1);
    codegen.generateParagraph(para_str);
    delete $1;
}

inline_formatting: BOLD {
    string& bold_str = *$1;
    string content = bold_str.substr(2, bold_str.size() - 4);
    codegen.generateBold(content);
    delete $1;
}
                 | ITALIC {
    string& italic_str = *$1;
    string content = italic_str.substr(1, italic_str.size() - 2);
    codegen.generateItalic(content);
    delete $1;
}

%%

void yyerror(const char* s) {
    cerr << "语法错误: " << s << endl;
}

5. 实现C++代码生成模块

codegen.h

#ifndef CODEGEN_H
#define CODEGEN_H

#include <string>
#include <iostream>

class CodeGenerator {
public:
    CodeGenerator();
    void generateTitle(int level, const std::string& content);
    void generateParagraph(const std::string& content);
    void generateListItem(const std::string& content);
    void generateBold(const std::string& content);
    void generateItalic(const std::string& content);
    void endList();
    void generateHTMLHeader(const std::string& title = "Markdown转换结果");
    void generateHTMLFooter();

private:
    bool in_list_ = false;
};

#endif

codegen.cpp

#include "codegen.h"

CodeGenerator::CodeGenerator() {}

void CodeGenerator::generateHTMLHeader(const std::string& title) {
    std::cout << "<!DOCTYPE html>\n<html>\n<head>\n<title>" << title << "</title>\n</head>\n<body>\n";
}

void CodeGenerator::generateHTMLFooter() {
    if (in_list_) {
        std::cout << "</ul>\n";
        in_list_ = false;
    }
    std::cout << "</body>\n</html>\n";
}

void CodeGenerator::generateTitle(int level, const std::string& content) {
    if (in_list_) {
        std::cout << "</ul>\n";
        in_list_ = false;
    }
    std::cout << "<h" << level << ">" << content << "</h" << level << ">\n";
}

void CodeGenerator::generateParagraph(const std::string& content) {
    if (in_list_) {
        std::cout << "</ul>\n";
        in_list_ = false;
    }
    std::cout << "<p>" << content << "</p>\n";
}

void CodeGenerator::generateListItem(const std::string& content) {
    if (!in_list_) {
        std::cout << "<ul>\n";
        in_list_ = true;
    }
    std::cout << "<li>" << content << "</li>\n";
}

void CodeGenerator::endList() {
    if (in_list_) {
        std::cout << "</ul>\n";
        in_list_ = false;
    }
}

void CodeGenerator::generateBold(const std::string& content) {
    std::cout << "<strong>" << content << "</strong>";
}

void CodeGenerator::generateItalic(const std::string& content) {
    std::cout << "<em>" << content << "</em>";
}

6. 实现主程序入口(main.cpp)

负责文件读取、分析器启动与HTML头尾生成:

#include <iostream>
#include <fstream>
#include "md.tab.h"
#include "codegen.h"

extern FILE* yyin;
extern CodeGenerator codegen;

int main(int argc, char* argv[]) {
    if (argc != 2) {
        std::cerr << "用法: " << argv[0] << " <输入Markdown文件>\n";
        return 1;
    }

    yyin = fopen(argv[1], "r");
    if (!yyin) {
        std::cerr << "无法打开文件: " << argv[1] << "\n";
        return 1;
    }

    codegen.generateHTMLHeader();
    yyparse();
    codegen.generateHTMLFooter();

    fclose(yyin);
    return 0;
}

7. 编译与运行流程

  1. 生成Flex词法分析器代码:
flex -o md.yy.cpp md.l
  1. 生成Bison语法分析器代码:
bison -d -o md.tab.cpp md.y
  1. 编译所有源文件:
g++ -o md2html main.cpp md.yy.cpp md.tab.cpp codegen.cpp
  1. 测试转换:
./md2html input.md > output.html

8. 调试与扩展建议

  • 调试:在Flex规则中添加cout << "Token: " << yytext << endl;打印识别的token,在Bison动作中添加调试输出确认语义处理逻辑。
  • 扩展:逐步添加链接、图片、块引用等特性,只需在Flex中新增token规则,Bison中补充语法规则,代码生成模块新增对应HTML标签生成方法。
  • 注意:处理HTML特殊字符(如<、>、&)时,需转义为对应实体(如&lt;、&gt;、&amp;)避免解析错误。

内容的提问来源于stack exchange,提问作者Manish Kumar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 00:23:19