论坛首页 安全编程开发区 阅读主题

[分享]简单编译器实现

396 浏览 2 回复
#1 楼主 2026-06-01 21:08:48
前言
前几小结实现了简单的代码解释器、词法解析器,由以上基础铺垫 本节笔者将实现一个简易的编译器,实现效果为:编写简单代码 "a = 1 + 2" 编译器需要使用词法解析器将其解析成 token 序列,再将 token 序列建立 AST 节点,例如:
/ \
a +
/ \
1 2

这个“树结构”,就是 AST(Abstract Syntax Tree,抽象语法树)
其中涉及数字节点、二元表达式以及赋值节点
为什么要建立 AST 节点呢? 因为当一个表达式 a = 1 + 2 程序并不知道先处理 = 还是处理 + ,以后还会实现更复杂的表达式 AST 节点可以很好解决优先级处理问题
有了 AST 节点后就能编写代码生成器,将 AST 转换为 虚拟机指令(MOV / ADD),最后输出机器码即可放入之前写的解释器中运行
本节将以最简编译器的方式走通从源码到结果的全流程,大致流程如下:
ource Code(源码)

Lexer(词法分析 把字符串 → 切成“最小语法单元”)

Token(中间数据)

Parser(语法分析)

AST(抽象语法树)

CodeGen(代码生成)

指令(IR / VM 指令)

执行(解释器 / 虚拟机)

结果

一、词法分析
将调用上节的代码进行复用
// 1. 词法分析
Lexer lexer(input);
auto tokens = lexer.tokenize();

二、语法分析
目前只处理了 + 和 - 操作 以及 数字 + 数字 的形式,其余可自行补充
//语法解析
class Syntax {
public:
Syntax(const std::vector<Token>& tokens)
: tokens(tokens), pos(0) {
//解析
AssignStmt parse() {
AssignStmt stmt;

// a
stmt.name = consume(TOK_VAR).text;

// = 这里没有保存值,只是“确认语法正确”
consume(TOK_ASSIGN);

// 1 + 2 解析表达式
stmt.expr = parseExpr();

return stmt;

private:
std::vector<Token> tokens;
size_t pos;
//当前pos指向的token
Token& peek() {
return tokens[pos];
//验证 + 前进
Token consume(TokenType type) {
if (tokens[pos].type != type) {
throw std::runtime_error("unexpected token");
return tokens[pos++];

// 只实现 1 + 2 这种
Expr* parseExpr() {
//解析左边
Expr* left = parsePrimary();
//判断是否有 +
if (peek().type == TOK_ADD) {
//处理 +
consume(TOK_ADD);
//解析右边
Expr* right = parsePrimary();
//构建 BinaryExpr: 1+2
return new BinaryExpr('+', left, right);
//判断是否有 -
if (peek().type == TOK_SUB) {
//处理 -
consume(TOK_SUB);
//解析右边
Expr* right = parsePrimary();
//构建 BinaryExpr: 1+2
return new BinaryExpr('-', left, right);

return left;
//解析“最小单位” 把简单元素组合成表达式
Expr* parsePrimary() {
if (peek().type == TOK_NUMBER) {
//stoi: 字符转换整数
int v = std::stoi(consume(TOK_NUMBER).text);
return new NumberExpr(v);

throw std::runtime_error("invalid expression");

三、代码生成
代码生成目前只支持 + 和 - 操作,剩余可自行添加
class CodeGen {
public:

void gen(const AssignStmt& stmt) {
//把表达式计算结果放到 R0
genExpr(stmt.expr);

// 这里只是演示,没有变量表
// 先直接 print 结果
PRINT(as, R0);
HALT(as);

private:
Assembler& as;
//递归生成表达式的指令
void genExpr(Expr* expr) {
//数字
//dynamic_cast: 判断 expr 是不是 NumberExpr 类型
if (auto num = dynamic_cast<NumberExpr*>(expr)) {
//生成:MOV R0, 1
MOVI(as, R0, num->value);
//二元表达式 expr 是 1 + 2
else if (auto bin = dynamic_cast<BinaryExpr*>(expr)) {
// 左边 → R0
genExpr(bin->left);

// 右边 → R1
int value = getNumber(bin->right);//把右边表达式“强行当成数字”
MOVI(as, R1, value);

////处理+
//if (bin->op == '+') {
// ADDR(as, R0, R1);
//}
switch (bin->op)
case '+': {
ADDR(as, R0, R1);
break;
case '-': {
SUBR(as, R0, R1);
break;


...(已截断)

---
来源: 看雪论坛
原文链接: https://bbs.kanxue.com/thread-290892.htm
#2 2026-06-01 21:08:48
你这不叫编译器,你这叫语法解析器
#3 2026-06-01 21:08:48
LyOnline

你这不叫编译器,你这叫语法解析器
是的 前半段的Lexer只负责分析语法,真正编译成机器码的过程在CodeGen 第三部分, 最后由自定义的解释器解释

请登录后参与讨论

立即登录 注册账号