Use when explaining System V AMD64, ARM AAPCS, RISC-V psABI, stack frames, variadic calls, or FFI register rules. Not for the Rust FFI binding layer: use rust-ffi.
日本語の概要は準備中です。原文の説明を表示しています。
Use when building a lexer, Pratt or recursive-descent parser, AST, symbol table, type checker, or LLVM IR emitter for a language or DSL. Not for optimizing IR: use llvm-passes.
インストールする前に、エージェントに与えられる指示の中身を確認できます。
| Field | Bound contract |
|---|---|
| Trigger | A user implements a language or DSL, adds expression parsing to an interpreter or config format, designs AST nodes in C or Rust, needs scoped symbols, basic type checking, error recovery, or wants a typed AST lowered to LLVM IR. |
| Authority | Reversible local: writes only the frontend source files the user names inside the project; rollback is version control. No remote mutation. |
| Side effect | Frontend source files are created or edited; a test input is lexed, parsed, checked, and, when requested, emitted as IR and verified. |
| Done | Each requested stage compiles, runs on the supplied test inputs, and the verification named for that stage passes: token stream matches, precedence tests pass, undefined and mismatched types are reported, generated IR passes the LLVM verifier. |
llvm-c/; in Rust use the inkwell or llvm-sys crates.Fix the pipeline: source to lexer (tokens) to parser (AST) to type checker to IR generator to LLVM IR. Write down which stages this task delivers. Done when: the delivered stages and their input and output types are listed.
Write the lexer as a hand-written state machine. Tokens carry a kind, a pointer into the source, a length, and any literal value. Skip whitespace, then dispatch on the first character: digits build an integer, letters build an identifier, single characters map to punctuation.
typedef enum { TOK_EOF, TOK_INT, TOK_IDENT, TOK_PLUS, TOK_MINUS,
TOK_LPAREN, TOK_RPAREN, TOK_SEMI, TOK_EQ, TOK_RETURN } 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;
Token lexer_next(Lexer *l) {
while (l->src[l->pos] == ' ' || l->src[l->pos] == '\n') l->pos++;
const char *start = &l->src[l->pos];
if (isdigit((unsigned char)l->src[l->pos])) {
int64_t val = 0;
while (isdigit((unsigned char)l->src[l->pos]))
val = val * 10 + (l->src[l->pos++] - '0');
return (Token){ TOK_INT, start, (int)(&l->src[l->pos] - start), val };
}
if (isalpha((unsigned char)l->src[l->pos])) {
while (isalnum((unsigned char)l->src[l->pos])) l->pos++;
return (Token){ TOK_IDENT, start, (int)(&l->src[l->pos] - start), 0 };
}
switch (l->src[l->pos++]) {
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 };
}
}
A generated lexer is the alternative when the token set is large: flex lexer.l && gcc -o lexer lex.yy.c -lfl. With flex, order rules so the longest match wins and keywords precede the identifier rule. Done when: every test input produces the expected token stream, including the EOF token.
Parse expressions with a Pratt parser. Each infix operator has a left and a right binding power; a higher power binds tighter, and right associativity is a right power one below the left. The loop consumes operators while their left power is at least the minimum.
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;
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;
int op = p->cur.kind;
advance(p);
AstNode *right = parse_expression(p, rbp);
left = make_binop(op, left, right);
}
return left;
}
Capture the operator before advance; reading p->cur.kind after the recursive call returns the wrong token. Done when: a table of expressions with expected trees passes, including mixed precedence and associativity cases.
Parse statements with recursive descent, one function per nonterminal. Every branch must consume at least one token or return; a branch that neither advances nor returns is the cause of an infinite loop.
AstNode *parse_statement(Parser *p) {
if (p->cur.kind == TOK_IDENT && peek(p) == TOK_EQ) {
char *name = take_ident(p);
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);
}
Done when: every statement form in the test inputs parses and every branch is audited for token progress.
Build the symbol table as a chain of scopes. Lookup walks from the innermost scope to the root; definition writes only into the current scope, so shadowing an outer name is allowed and redefining a name in the same scope is an error.
typedef struct Symbol { char *name; Type *type; LLVMValueRef llvm_val; struct Symbol *next; } Symbol;
typedef struct Scope { Symbol *symbols; 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 *sym);
if (!sym) abort();
sym->name = strdup(name); sym->type = type; sym->llvm_val = NULL;
sym->next = s->symbols; s->symbols = sym;
}
Replace the linked list with a hash map when scopes hold more than a few dozen names. Done when: shadowing resolves to the inner name and a same-scope duplicate is rejected.
Type-check by walking the AST and returning a type per node. Integers have the integer type; a variable takes its symbol's type or reports "undefined"; a binary operation requires equal operand types.
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();
}
For an ML-style language with inference, assign a type variable to each unknown and unify on constraints (Hindley-Milner); this is a separate stage, not a change to the checker above. Done when: each error test input produces the expected diagnostic and valid inputs pass.
Recover from syntax errors with panic mode: on error, advance until a synchronizing token, then resume. Pick the synchronizing set per nonterminal (statement end ;, block end }, statement-start keywords).
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);
}
}
Error productions for common mistakes and single-token insertion or deletion are the next steps when the frontend serves an editor. Done when: an input with two independent errors reports both, not a cascade from the first.
Emit LLVM IR from the typed AST through the C API. Create types from a context, build instructions through a builder, and use the typed load: the untyped LLVMBuildLoad is gone from the current C API (LLVM 23.1.0) and LLVMInt32Type() without a context is deprecated.
#include <llvm-c/Core.h>
#include <llvm-c/Analysis.h>
LLVMContextRef ctx; LLVMModuleRef module; LLVMBuilderRef builder;
LLVMValueRef codegen_expr(Scope *s, AstNode *node) {
switch (node->kind) {
case AST_INT:
return LLVMConstInt(LLVMInt32TypeInContext(ctx), (unsigned long long)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 LLVMBuildLoad2(builder, LLVMInt32TypeInContext(ctx), sym->llvm_val, node->name);
}
}
return NULL;
}
After emitting, dump and verify: LLVMDumpModule(module); and LLVMVerifyModule(module, LLVMReturnStatusAction, &err); where LLVMVerifyModule is declared in llvm-c/Analysis.h. Done when: the verifier returns success on every valid test input.
| Failure class | Behavior |
|---|---|
| Parser loops forever | A branch consumed no token. Audit every branch of the failing nonterminal for advance or return; add the missing progress. |
| Wrong precedence | Binding powers are wrong. Fix the table and re-run the expression table test. |
| Duplicate symbol accepted or shadowing rejected | Lookup and define use the wrong scope. Lookup walks parents; define writes the current scope only. |
| LLVM verifier fails | An IR type mismatch, usually signedness, pointer level, or a load with the wrong type argument. Read the verifier message; fix the emitter for that node kind. |
| flex token clash | Overlapping patterns. Rely on longest match and order keyword rules before the identifier rule. |
| Error cascade | No synchronization. Implement panic-mode recovery per step 7. |
No partial result is claimed complete. If a stage cannot finish, the report states which stages passed their verification and which are blocked.
A frontend delivery containing:
まだレビューはありません。使ってみた感想をお寄せください。
概要と使いどころ
Use when explaining System V AMD64, ARM AAPCS, RISC-V psABI, stack frames, variadic calls, or FFI register rules. Not for the Rust FFI binding layer: use rust-ffi.
日本語の概要は準備中です。原文の説明を表示しています。
Use when configuring ADC sampling time, DMA-driven ADC, calibration, or DAC channel setup on bare-metal MCUs. Not for the DMA stream itself: use dma-baremetal.
日本語の概要は準備中です。原文の説明を表示しています。
Use when creating AF_XDP sockets, configuring UMEM and XSK rings, writing an XDP redirect program, or choosing copy versus zero-copy mode. Not for full kernel bypass: use dpdk.
日本語の概要は準備中です。原文の説明を表示しています。
Use when a completed session needs an agent-environment retrospective. Not for an engineering retrospective from telemetry: use engineering-retrospective.
日本語の概要は準備中です。原文の説明を表示しています。
Use when a redacted, trimmed agent transcript must be appended to a GitHub PR or issue body, with human approval and preview. Not for automated or model-initiated insertion.
日本語の概要は準備中です。原文の説明を表示しています。
Use when a repo needs agent setup, AGENTS.md added or made lean, CLAUDE.md audited, or agent instructions scored or pruned. Not for remote, credential, publish, deploy, or irreversible changes.
日本語の概要は準備中です。原文の説明を表示しています。