关于 compiler-frontend
compiler-frontend 是一项面向构建编译器前端的 Agent 技能,适用于 C 或 Rust 项目,涵盖从词法与语法分析到类型检查、错误恢复及 LLVM IR 生成。
主要功能
- 01使用 DFA 或 flex 设计词法分析器
- 02使用 Pratt 方法解析表达式
- 03递归下降解析语句
- 04设计 AST 与作用域符号表
- 05类型检查与错误恢复
- 06生成 LLVM IR
适用场景
- 01实现编程语言或 DSL
- 02为解释器添加表达式解析
- 03构建带作用域的符号表
- 04从类型化 AST 生成 LLVM IR
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ mohitmishra786/ low-level-dev-skills --skill compiler-frontend安装后在 Claude Code 中输入/compiler-frontend确认已加载。
详细介绍
编译器前端
用途
指导 Agent 构建编译器前端:词法分析器(手写 DFA 与 flex)、表达式的 Pratt 解析、语句的递归下降解析、AST 设计、使用带作用域的哈希映射构建符号表、类型检查基础、错误恢复策略,以及通过 C API 或 llvm-sys 生成 LLVM IR。
适用场景
- 实现新的编程语言或 DSL
- 为解释器或配置语言添加表达式解析
- 在 C 或 Rust 中设计 AST 节点层级
- 为变量和函数构建带作用域的符号表
- 实现基本的类型推断或类型检查
- 从带类型的 AST 生成 LLVM IR
工作流
1. 流水线概览
Source → Lexer (tokens) → Parser (AST) → Type checker → IR generator → LLVM IR
2. 词法分析器 — 手写 DFA
typedef enum {
TOK_EOF, TOK_INT, TOK_IDENT, TOK_PLUS, TOK_MINUS,
TOK_LPAREN, TOK_RPAREN, TOK_SEMI, TOK_EQ,
} TokenKind;
typedef struct {
TokenKind kind;
const char *start;
int length;
int64_t int_val;
} Token;
typedef struct {
const char *src;
int pos;
int line;
} Lexer;
static void skip_whitespace(Lexer *l) {
while (l->src[l->pos] == ' ' || l->src[l->pos] == '\n') l->pos++;
}
Token lexer_next(Lexer *l) {
skip_whitespace(l);
const char *start = &l->src[l->pos];
if (isdigit(l->src[l->pos])) {
int64_t val = 0;
while (isdigit(l->src[l->pos]))
val = val * 10 + (l->src[l->pos++] - '0');
return (Token){ TOK_INT, start, l->pos - (start - l->src), val };
}
if (isalpha(l->src[l->pos])) {
while (isalnum(l->src[l->pos])) l->pos++;
return (Token){ TOK_IDENT, start, l->pos - (start - l->src), 0 };
}
char c = l->src[l->pos++];
switch (c) {
case '+': return (Token){ TOK_PLUS, start, 1, 0 };
case '(': return (Token){ TOK_LPAREN, start, 1, 0 };
case ')': return (Token){ TOK_RPAREN, start, 1, 0 };
case ';': return (Token){ TOK_SEMI, start, 1, 0 };
default: return (Token){ TOK_EOF, start, 0, 0 };
}
}
# flex alternative
flex lexer.l && gcc -o lexer lexer.tab.c -lfl
3. 表达式的 Pratt 解析
typedef enum { AST_INT, AST_BINOP, AST_VAR } AstKind;
typedef struct AstNode {
AstKind kind;
union {
int64_t int_val;
struct { int op; struct AstNode *lhs, *rhs; } binop;
char *name;
};
} AstNode;
// Binding powers: higher = tighter precedence
enum { BP_NONE = 0, BP_SUM = 10, BP_PRODUCT = 20 };
AstNode *parse_expression(Parser *p, int min_bp) {
AstNode *left = parse_prefix(p);
for (;;) {
int lbp, rbp;
if (!infix_binding_power(p->cur.kind, &lbp, &rbp) || lbp < min_bp)
break;
advance(p);
AstNode *right = parse_expression(p, rbp);
left = make_binop(p->cur.kind, left, right);
}
return left;
}
Pratt 解析可以简洁地处理运算符优先级,无需庞大的语法表。
4. 语句的递归下降解析
AstNode *parse_statement(Parser *p) {
if (match(p, TOK_IDENT) && peek(p) == TOK_EQ) {
char *name = p->prev.text;
advance(p); // =
AstNode *expr = parse_expression(p, BP_NONE);
expect(p, TOK_SEMI);
return make_assign(name, expr);
}
if (match(p, TOK_RETURN)) {
AstNode *expr = parse_expression(p, BP_NONE);
expect(p, TOK_SEMI);
return make_return(expr);
}
return parse_expression_statement(p);
}
5. AST 和符号表
typedef struct Symbol {
char *name;
Type *type;
LLVMValueRef llvm_val; // after codegen
struct Symbol *next;
} Symbol;
typedef struct Scope {
Symbol *symbols; // hash map bucket chain
struct Scope *parent;
} Scope;
Symbol *scope_lookup(Scope *s, const char *name) {
for (Scope *cur = s; cur; cur = cur->parent) {
for (Symbol *sym = cur->symbols; sym; sym = sym->next)
if (strcmp(sym->name, name) == 0)
return sym;
}
return NULL;
}
void scope_define(Scope *s, const char *name, Type *type) {
Symbol *sym = malloc(sizeof(Symbol));
sym->name = strdup(name);
sym->type = type;
sym->next = s->symbols;
s->symbols = sym;
}
6. 类型检查器(基础)
typedef enum { TY_INT, TY_BOOL, TY_FUNC, TY_VOID } TypeKind;
Type *check_expr(Scope *s, AstNode *node) {
switch (node->kind) {
case AST_INT: return type_int();
case AST_VAR: {
Symbol *sym = scope_lookup(s, node->name);
if (!sym) error("undefined variable %s", node->name);
return sym->type;
}
case AST_BINOP: {
Type *lt = check_expr(s, node->binop.lhs);
Type *rt = check_expr(s, node->binop.rhs);
if (!type_equal(lt, rt))
error("type mismatch in binary op");
return lt;
}
}
return type_void();
}
Hindley-Milner(完整推断):分配类型变量,并根据约束进行合一——适用于类 ML 语言。
7. 错误恢复
| 策略 | 适用场景 |
|---|---|
| 恐慌模式 | 跳过标记,直到遇到同步标记(;、}) |
| 同步集合 | 为每个非终结符定义恢复标记 |
| 错误产生式 | 为常见错误编写语法规则 |
| 单标记插入/删除 | 适用于 IDE 的恢复方式 |
void synchronize(Parser *p) {
advance(p);
while (p->cur.kind != TOK_EOF) {
if (p->prev.kind == TOK_SEMI) return;
if (p->cur.kind == TOK_RETURN || p->cur.kind == TOK_IDENT) return;
advance(p);
}
}
8. LLVM IR 生成
#include <llvm-c/Core.h>
#include <llvm-c/ExecutionEngine.h>
LLVMModuleRef module;
LLVMBuilderRef builder;
LLVMValueRef codegen_expr(Scope *s, AstNode *node) {
switch (node->kind) {
case AST_INT:
return LLVMConstInt(LLVMInt32Type(), node->int_val, 1);
case AST_BINOP: {
LLVMValueRef l = codegen_expr(s, node->binop.lhs);
LLVMValueRef r = codegen_expr(s, node->binop.rhs);
if (node->binop.op == TOK_PLUS)
return LLVMBuildAdd(builder, l, r, "add");
break;
}
case AST_VAR: {
Symbol *sym = scope_lookup(s, node->name);
return LLVMBuildLoad(builder, sym->llvm_val, node->name);
}
}
return NULL;
}
# Dump IR
LLVMDumpModule(module);
# Verify
char *err = NULL;
LLVMVerifyModule(module, LLVMReturnStatusAction, &err);
Rust:使用 inkwell 或 llvm-sys crate,获得更安全的绑定。
常见问题
| 症状 | 原因 | 修复方法 |
|---|---|---|
| 解析器无限循环 | 出错时未前进 | 检查所有分支是否都会前进到下一个标记 |
| 运算符优先级错误 | 绑定力设置不正确 | 使用表格测试表达式 |
| 作用域内符号重复 | 未检查遮蔽 | 读取时只查找父作用域;定义时仅查找当前作用域 |
| LLVM 验证失败 | IR 中的类型不匹配 | 检查有无符号性和指针层级 |
| flex 标记冲突 | 模式重叠 | 使用最长匹配;调整规则顺序 |
| 错误级联 | 没有同步机制 | 实现恐慌模式恢复 |
相关技能
skills/compiler-internals/llvm-passes— 优化生成的 IRskills/compiler-internals/jit-compilation— 执行生成的 IRskills/compiler-internals/mlir— 用于 DSL 的更高级 IRskills/compilers/llvm— 作为 LLVM 用户使用 opt、llcskills/low-level-programming/interpreters— 字节码 VM 替代方案skills/languages/carbon-lang— 现代语言前端示例
评论
支持 Markdown