SkillAgentSearch skills...

compiler-frontend

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.

Install / Use

npx skills add OutlineDriven/outline-driven-development --skill compiler-frontend

Installs into whichever agent you are using.

About this skill
📄

SKILL.md

Installable skill definition

Quality Score

60/100

Supported Platforms

Universal

Tags

Our assessment of compiler-frontend

compiler-frontend scores 60/100 on our quality scale, 4063rd of 4,599 Development & Engineering skills we index.

Its SKILL.md is 11 KB long, split into 6 sections and no code examples: a thorough specification that gives an agent plenty to work with.

It has no GitHub stars yet, so there is no community track record; judge it on its content.

Substance
29/30
Structure
11/20
Description
15/15
Adoption
0/20
Freshness
5/15

Maintenance, license and trust

  • We could not determine when the repository was last updated.
  • Our last check on 2026-10-20 found the source still online.
  • No license is declared. By default that means all rights are reserved: you can read it, but reusing or redistributing it is not clearly permitted. Ask the author before building on it commercially.
  • Its trust signals score 68/100, with 3 cautions from licensing, adoption, age or documentation. These come from repository metadata, not a code audit — read the skill file before letting an agent act on it.

Safety scan

No issues found

Our scan of the whole file found no instruction hijacking, hidden characters, credential access, data exfiltration or destructive commands. An AI review of the same text found nothing harmful.

AI review by kimi-k2.7-code on 2026-09-24. Automated pattern scan on 2026-09-24. It catches known dangerous patterns, not every risk — read a skill before letting an agent act on it.

compiler-frontend compared with similar skills

All 4 of these similar skills score higher than compiler-frontend; compare them before choosing.

SkillScoreStarsUpdatedFormat
compiler-frontend (this skill)by OutlineDriven600—SKILL.md
taste-skillby Leonxlnx10099.8ktodayCLAUDE.md
ai-job-searchby MadsLorentzen10060.2ktodayCLAUDE.md
claude-howtoby luongnv8910042.1ktodayCLAUDE.md
algorithmic-artby anthropics100177.9k28d agoSKILL.md

Frequently asked questions

How do I install compiler-frontend?
Run npx skills add OutlineDriven/outline-driven-development --skill compiler-frontend. The install tabs above show the steps for each supported agent.
Which AI agents does compiler-frontend work with?
It is written for Universal, as a SKILL.md file. Other agents that read the same format can often use it too.
Is compiler-frontend safe to use?
Our scan of the whole file found no instruction hijacking, hidden characters, credential access, data exfiltration or destructive commands. An AI review of the same text found nothing harmful. It declares no license and scores 68/100 on trust signals. Skills are instructions an agent will follow, so read the file before installing it and do not approve commands you do not understand.
Is compiler-frontend still maintained?
We could not determine when the repository was last updated.

name: compiler-frontend description: '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.'

Compiler frontend

Contract

| 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. |

Inputs

  1. Language description (required): the token set, the grammar or a set of example programs, and the operators with their precedence and associativity.
  2. Implementation language (required): C or Rust. C samples below use the LLVM C API in llvm-c/; in Rust use the inkwell or llvm-sys crates.
  3. Requested stages (required): any subset of lexer, parser, symbol table, type checker, IR emitter.
  4. Test inputs (required): at least one valid program per stage and one program with an error the stage must report.

Procedure

  1. 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.

  2. 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.

  3. 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.

  4. 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.

  5. 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.

  6. 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.

  7. 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.

  8. 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 and recovery

| 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.

Output

A frontend delivery containing:

  1. Files written: each source file created or edited, per stage.
  2. Verification: per stage, the test inputs run and the observed token stream, tree, diagnostic, or verifier result.
  3. Open decisions: any grammar ambiguity or type rule the language description left undefined, with the choice made.

Related Skills

View on GitHub
GitHub Stars0
CategoryDevelopment
UpdatedNaNy ago
Forks0

Trust signals

68/100

From repository metadata: license, adoption, age and documentation. Not a code audit — see the Safety scan above for what the skill file itself contains.

2 medium1 low
compiler-frontend — Universal Skill: Install & Safety Check | SkillAgent