mcpField
C
compiler-frontend

compiler-frontend

github.com/mohitmishra786開発269311 回インストール18 週間前に更新
リポジトリ

compiler-frontend について

compiler-frontend は、C または Rust でコンパイラフロントエンドを構築するエージェント向けのスキルです。字句・構文解析から型チェック、エラー回復、LLVM IR 生成までを扱います。

主な機能

  1. 01DFA または flex による字句解析器設計
  2. 02Pratt 法による式解析
  3. 03再帰下降による文解析
  4. 04AST とスコープ付きシンボルテーブル
  5. 05型チェックとエラー回復
  6. 06LLVM IR の生成

活用シーン

  1. 01プログラミング言語や DSL の実装
  2. 02インタープリターへの式解析追加
  3. 03スコープ付きシンボルテーブルの構築
  4. 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 向けの高水準 IR
  • skills/compilers/llvm — LLVM をユーザーとして使用する(opt、llc)
  • skills/low-level-programming/interpreters — バイトコード VM の代替
  • skills/languages/carbon-lang — 最新の言語フロントエンドの例

コメント