compiler-frontend について
compiler-frontend は、C または Rust でコンパイラフロントエンドを構築するエージェント向けのスキルです。字句・構文解析から型チェック、エラー回復、LLVM IR 生成までを扱います。
主な機能
- 01DFA または flex による字句解析器設計
- 02Pratt 法による式解析
- 03再帰下降による文解析
- 04AST とスコープ付きシンボルテーブル
- 05型チェックとエラー回復
- 06LLVM 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を入力して読み込まれたか確認します。
詳細
コンパイラフロントエンド
目的
コンパイラフロントエンドの構築をエージェントに案内します。レクサー(手書きの 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. エラー回復
| 戦略 | 使用する場面 |
|---|---|
| パニックモード | 同期トークン(;、})までトークンを読み飛ばす |
| 同期集合 | 非終端記号ごとに回復用トークンを定義する |
| エラー生成規則 | よくある間違いに対応する文法規則 |
| 1 トークンの挿入/削除 | 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 クレートを使用します。
よくある問題
| 症状 | 原因 | 対処法 |
|---|---|---|
| パーサーが無限ループする | エラー時に advance していない | すべての分岐でトークンを進めているか確認する |
| 演算子の優先順位が誤っている | 結合力が正しくない | 式を表形式でテストする |
| スコープ内でシンボルが重複する | シャドーイングのチェックがない | 読み取り時は親スコープのみを検索し、定義は現在のスコープで行う |
| LLVM の検証に失敗する | IR 内の型が一致しない | 符号の有無とポインタのレベルを確認する |
| flex のトークンが競合する | パターンが重複している | 最長一致を使い、規則の順序を調整する |
| エラーが連鎖する | 同期処理がない | パニックモードの回復を実装する |
関連スキル
skills/compiler-internals/llvm-passes— 生成された IR を最適化するskills/compiler-internals/jit-compilation— 生成された IR を実行するskills/compiler-internals/mlir— DSL 向けの高水準 IRskills/compilers/llvm— LLVM をユーザーとして使用する(opt、llc)skills/low-level-programming/interpreters— バイトコード VM の代替skills/languages/carbon-lang— 最新の言語フロントエンドの例
コメント
Markdown 対応