parser-developer
Skill38 starsChanged 6 days agoApache-2.0View on GitHub
Guides AILANG parser development with conventions and patterns. Use when user wants to modify parser, understand parser architecture, or debug parser issues. Saves 30% of development time by preventing token position bugs.
In this repository85 other files it gives its agents.
- AGENTS.mdAGENTS.md
- CLAUDE.mdCLAUDE.md
- docs/llms.txtllms.txt
- llms.txtllms.txt
- agent-inboxSkill
- ailang-core-triageSkill
- ailang-packagesSkill
- benchmark-managerSkill
- benchmark-runnerSkill
- builtin-developerSkill
- cli-doc-maintainerSkill
- cloud-cascade-debugSkill
- cloud-setupSkill
- codebase-organizerSkill
- collaboration-hubSkill
- coordinator-helperSkill
- design-doc-creatorSkill
- design-spec-auditorSkill
- docs-syncSkill
- eval-analyzerSkill
- eval-gap-finderSkill
- gemini-cli-helperSkill
- github-issue-triageSkill
- headless-runnerSkill
- local-model-onboardingSkill
- local-ollama-evalSkill
- mission-briefSkill
- mission-controlSkill
- mission-loop-changeSkill
- model-managerSkill
- motoko-analyzerSkill
- post-releaseSkill
- pr-monitorSkill
- prompt-managerSkill
- release-managerSkill
- sonarcloud-triageSkill
- sprint-evaluatorSkill
- sprint-executorSkill
- sprint-plannerSkill
- test-coverage-guardianSkill
- trace-debuggerSkill
- use-ailangSkill
- wasm-debuggerSkill
- agent-inboxSkill
- ailang-core-triageSkill
- ailang-packagesSkill
- benchmark-managerSkill
- benchmark-runnerSkill
- builtin-developerSkill
- cli-doc-maintainerSkill
- cloud-cascade-debugSkill
- cloud-setupSkill
- codebase-organizerSkill
- collaboration-hubSkill
- coordinator-helperSkill
- design-doc-creatorSkill
- design-spec-auditorSkill
- docs-syncSkill
- eval-analyzerSkill
- eval-gap-finderSkill
- The first 60 shown.
The file
We split this skill into the rules it gives an agent
5 rules. About 0 are common, 0 are distinctive, and 5 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 21
Use DEBUG_PARSER=1 for token tracing
- Very specific: line 22
Use make doc PKG=<package> for API discovery
- Very specific: line 64
Run parser with DEBUG_PARSER=1 to trace token positions.
- Very specific: line 162
Only logs when DEBUG_PARSER=1 is set (zero overhead otherwise)
- Very specific: line 397
Use make doc PKG=<package> for API discovery (80% faster)
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Parser Developer
Master AILANG parser development with critical conventions that prevent common bugs.
Quick Start
⚠️ READ THIS BEFORE WRITING PARSER CODE
This skill documents critical parser conventions that prevent token position bugs (the #1 time sink in parser development).
Time savings: ~30% by avoiding common pitfalls
Key conventions:
- Parser leaves cursor AT last token (not after)
- Lexer never generates NEWLINE tokens
- Use
DEBUG_PARSER=1for token tracing - Use
make doc PKG=<package>for API discovery
When to Use This Skill
Invoke this skill when:
- User wants to modify the AILANG parser
- User asks about parser architecture or conventions
- User is debugging parser issues
- User needs to understand token positioning
- User wants to add new syntax to AILANG
- User asks "how do I parse...?"
Critical Convention: Token Position
CRITICAL: AILANG parser functions follow this convention:
- Input: Parser is AT the first token to parse
- Output: Parser is AT the last token of what was parsed (NOT after it)
Example:
// To parse "42" followed by a comma:
p.nextToken() // move to 42
expr := p.parseExpression(LOWEST) // parses "42", leaves cur=42 (NOT comma!)
p.nextToken() // NOW we're at comma ✓Functions following this convention:
parseExpression()- Leaves parser AT the last token of the expressionparseType()- Leaves parser AT the last token of the typeparsePattern()- Leaves parser AT the last token of the pattern- Most parser functions follow this pattern
When writing new parser functions:
- ✅ ALWAYS call
p.nextToken()AFTER calling these functions - ❌ DON'T call
p.nextToken()BEFORE - the caller handles positioning - ✅ Document your function if it deviates from this convention
See: resources/token_positioning.md for detailed examples
Available Scripts
scripts/trace_parser.sh <file.ail>
Run parser with DEBUG_PARSER=1 to trace token positions.
Usage:
.Codex/skills/parser-developer/scripts/trace_parser.sh test.ailOutput:
[ENTER parseExpression] cur=INT(42) peek=COMMA
[EXIT parseExpression] cur=INT(42) peek=COMMAscripts/check_ast_types.sh
List all AST node types in the codebase.
Usage:
.Codex/skills/parser-developer/scripts/check_ast_types.shscripts/find_api.sh <package> <symbol>
Quick API discovery using make doc.
Usage:
.Codex/skills/parser-developer/scripts/find_api.sh internal/parser NewCommon AST Types
Quick type lookup:
grep "^type.*struct" internal/ast/ast.go | head -20Expression types:
- Literals:
ast.LiteralwithKindfield- ⚠️ GOTCHA: Lexer returns
int64, notintfor IntLit - ✅ Access:
lit.Value.(int64) - ❌ Wrong:
lit.Value.(int)(will panic!)
- ⚠️ GOTCHA: Lexer returns
- Lists:
ast.ListwithElements []Expr - Variables:
ast.VariablewithName string - Function calls:
ast.FuncCallwithFunc Expr, Args []Expr - Lambdas:
ast.LambdawithParams []*ast.Param, Body Expr - Blocks:
ast.BlockwithExprs []Expr
Type types:
- Simple types:
ast.SimpleTypewithName string - List types:
ast.ListTypewithElement Type - Function types:
ast.FuncTypewithParams []Type, Return Type, Effects *ast.EffectRow - Type applications:
ast.TypeAppwithCon string, Args []Type
Pattern types:
- Variable pattern:
ast.VarPatternwithName string - Constructor pattern:
ast.ConstructorPatternwithName string, Args []Pattern - Literal pattern:
ast.LiteralPatternwithValue Literal - Wildcard pattern:
ast.WildcardPattern(matches anything)
See: resources/ast_quick_reference.md for complete listing
Quick Token Lookup
Check if a keyword exists:
grep -i "forall" internal/lexer/token.go
# Output: FORALL token exists!Common testing keywords (already in lexer):
FORALL,EXISTS- QuantifiersTEST,TESTS- Test blocksPROPERTY,PROPERTIES- Property-based testsASSERT- Assertions
If you see an identifier instead of a keyword:
- ✅ Use
lexer.FORALL, notlexer.IDENT + literal check - ❌ Wrong:
p.curTokenIs(lexer.IDENT) && p.curToken.Literal == "forall" - ✅ Right:
p.curTokenIs(lexer.FORALL)
Debug Mode
Enable token position tracing:
DEBUG_PARSER=1 ailang run test.ailOutput example:
[ENTER parseType] cur=IDENT(int) peek=,
[EXIT parseType] cur=IDENT(int) peek=,
[ENTER parseExpression] cur=IDENT(x) peek=+
[EXIT parseExpression] cur=IDENT(x) peek=+How it works:
- Shows ENTER/EXIT for key parser functions
- Displays current (
cur) and next (peek) tokens - Only logs when
DEBUG_PARSER=1is set (zero overhead otherwise) - Output goes to stderr
See: resources/debug_mode.md for troubleshooting guide
Common Patterns
Pattern 1: Parsing Optional Sections
Pattern for parsing optional sections:
// properties can be in PEEK (no tests) or CUR (after tests)
if p.peekTokenIs(lexer.PROPERTIES) || p.curTokenIs(lexer.PROPERTIES) {
// If in peek, advance to it
if p.peekTokenIs(lexer.PROPERTIES) {
p.nextToken()
}
// Now always at PROPERTIES
properties := p.parsePropertiesBlock()
}Why: Previous optional section may or may not advance the parser, so check both positions.
Pattern 2: Test Error Printing
❌ WRONG - Errors are hidden:
if len(p.Errors()) != 0 {
t.Fatalf("parser had %d errors:", len(p.Errors()))
// ⚠️ This never executes! t.Fatalf stops immediately
for _, err := range p.Errors() {
t.Errorf(" %s", err)
}
}✅ CORRECT - Errors are visible:
if len(p.Errors()) != 0 {
// Print errors BEFORE Fatalf
for _, err := range p.Errors() {
t.Errorf(" %s", err)
}
t.Fatalf("parser had %d errors", len(p.Errors()))
}Pattern 3: String Formatting
⚠️ CRITICAL: string(rune(i)) produces unprintable characters!
// ❌ WRONG - Produces "\x01" instead of "1"
testName := "test_" + string(rune(i+1)) // BUG!
// ✅ CORRECT - Use fmt.Sprintf or strconv
testName := fmt.Sprintf("test_%d", i+1)
testName := "test_" + strconv.Itoa(i+1)Why: rune(1) is Unicode U+0001 (unprintable), not "1" (U+0031).
See: resources/common_patterns.md for more patterns
API Discovery Workflow
When you need to know an API:
1. Check make doc (fastest - 30 seconds)
make doc PKG=internal/parser | grep "parseExpression"
make doc PKG=internal/testing | grep "NewCollector"2. Check source files (if you know the file)
grep "^func New" internal/testing/collector.go
grep "^type.*struct" internal/ast/ast.go3. Check test files (shows real usage)
grep "NewCollector" internal/testing/*_test.go4. Check docs/guides/ (for complex workflows)
- docs/guides/parser_development.md
- docs/CONTRIBUTING.md
Time savings:
- Before
make doc: ~5-10 min per API lookup - After
make doc: ~30 sec per API lookup - Improvement: ~80% reduction
See: resources/api_discovery.md for constructor tables
Common Constructors
Quick reference:
| Package | Constructor | Signature | Notes |
internal/parser | New(lexer) | Takes lexer instance | Parser |
internal/elaborate | NewElaborator() | No arguments | Surface → Core |
internal/types | NewTypeChecker(core, imports) | Takes Core prog + imports | Type inference |
internal/link | NewLinker() | No arguments | Dictionary linking |
internal/testing | NewCollector(path string) | Takes module path | M-TESTING |
internal/eval | NewEvaluator(ctx) | Takes EffContext | Core evaluator |
See: resources/api_discovery.md for complete reference
Pipeline Sequence
Typical compilation pipeline:
// Step 1: Parse
l := lexer.New(input, "test.ail")
p := parser.New(l)
file := p.ParseFile()
// Step 2: Elaborate (Surface → Core)
elab := elaborate.NewElaborator() // ⚠️ No arguments!
coreProg, err := elab.Elaborate(file)
// Step 3: Type check
tc := types.NewTypeChecker(coreProg, nil) // nil = no imports
typedProg, err := tc.Check()
// Step 4: Link dictionaries
linker := link.NewLinker()
linkedProg, err := linker.Link(typedProg, tc.CoreTI)Common Field Gotchas
Use make doc to discover struct fields:
make doc PKG=internal/ast | grep -A 20 "type FuncDecl"Common mistakes:
// ✅ CORRECT
funcDecl.Tests // []*ast.TestCase (not .Tests.Cases!)
funcDecl.Properties // []*ast.Property
funcDecl.Params // []*ast.Param
// ❌ WRONG (fields that don't exist)
funcDecl.InlineTests // Use .Tests
funcDecl.Tests.Cases // .Tests is already the sliceResources
Detailed Guides
Token positioning:
- resources/token_positioning.md - Detailed examples and edge cases
AST types:
- resources/ast_quick_reference.md - Complete AST node listing
Common patterns:
- resources/common_patterns.md - Parser patterns and anti-patterns
API discovery:
- resources/api_discovery.md - Constructor tables and field reference
Debug mode:
- resources/debug_mode.md - Troubleshooting guide
Architecture Docs
- Parser design:
design_docs/planned/v0_3_15/m-dx9-parser-developer-experience.md - Contributing:
docs/CONTRIBUTING.md - Lexer/Parser:
internal/lexer/,internal/parser/
Critical Warnings
1. Lexer Never Generates NEWLINE Tokens
The lexer skips \n as whitespace. Even though lexer.NEWLINE exists, it's never generated!
❌ WRONG:
if p.curTokenIs(lexer.NEWLINE) { // This is NEVER true!
...
}✅ CORRECT:
// After RPAREN of Leaf(int), next token is PIPE (not NEWLINE)
if p.curTokenIs(lexer.PIPE) {
...
}Multi-line syntax "just works" because the lexer handles it.
2. IntLit is int64, not int
// ❌ WRONG - Will panic!
value := lit.Value.(int)
// ✅ CORRECT
value := lit.Value.(int64)3. Always Print Errors Before t.Fatalf
t.Fatalf stops execution immediately, so print errors first!
Progressive Disclosure
This skill loads information progressively:
- Always loaded: This SKILL.md file (conventions overview)
- Execute as needed: Scripts in
scripts/(tracing, API discovery) - Load on demand: Detailed guides in
resources/
Notes
- Parser functions leave cursor AT last token, not after
- Lexer never generates NEWLINE tokens - it skips them
- Use
DEBUG_PARSER=1for token tracing - Use
make doc PKG=<package>for API discovery (80% faster) - IntLit values are
int64, notint - Print errors before
t.Fatalfin tests
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

