002-parser-selection.md
April 10, 2026 ยท View on GitHub
Status
Accepted - February 2026
Context
The Temple DSL requires a production-grade parser capable of:
- Configurable Delimiters: Support {% raw %}
{% %}{% endraw %}, {% raw %}{{ }}{% endraw %}, {% raw %}{# #}{% endraw %} with user-configurable alternatives - Positional Accuracy: Track exact line/column positions for error reporting
- Incremental Parsing: Support LSP use cases (partial file edits)
- Embedded Languages: Distinguish template syntax from base format (Markdown, JSON, HTML)
- Error Recovery: Best-effort parsing with actionable error messages
Parser Options Evaluated
| Parser | Language | Approach | Type Safety | Performance | Ecosystem |
|---|---|---|---|---|---|
| Chevrotain | TypeScript | Code-based (Combinators) | Excellent (TS native) | High (benchmarked) | Active, well-maintained |
| Nearley | JavaScript | Grammar (BNF) | Good (TS definitions) | Moderate | Stable, less active |
| Peggy | JavaScript | PEG grammar | Good (generated code) | High | Active (Peg.js fork) |
| ANTLR4 | Multi-language | Grammar (ANTLR) | Good (generated TS) | High | Large ecosystem, Java-centric |
| Lark.js | JavaScript | Grammar (Lark port) | Poor (JS only) | Unknown | Port, not mature |
Detailed Analysis
Chevrotain
Pros:
- Pure TypeScript, zero dependencies
- Code-based grammar (no build step)
- Excellent error recovery and reporting
- Built-in support for token position tracking
- First-class TypeScript types for AST
- Active maintenance (SAP-backed)
- Benchmarks show 2-3x faster than ANTLR4
Cons:
- Steeper learning curve than declarative grammar formats
- Must write grammar as TypeScript code (not separate .grammar file)
Decision
Chevrotain as the primary parser for templ.js.
Rationale
- TypeScript-First: Code-based grammar provides full type safety and IDE support
- Zero Build Step: No grammar compilation means faster iteration
- Performance: Benchmarks show Chevrotain matches or exceeds alternatives
- Error Recovery: Built-in mechanisms for fault-tolerant parsing (critical for LSP)
- Position Tracking: First-class support for line/column tracking
- Maintenance: Well-documented, actively maintained, used in production (Monaco Editor)
Implementation Plan
- Tokenizer: Use Chevrotain's
Lexerclass with configurable delimiter patterns - Parser: Use Chevrotain's
CstParser(Concrete Syntax Tree) for maximum flexibility - AST Transformation: Post-process CST to Temple AST format
- Error Reporting: Leverage Chevrotain's error recovery for partial parses
Consequences
Positive
- Type Safety: Grammar rules are TypeScript functions with full type inference
- IDE Support: Grammar authoring gets IntelliSense, refactoring, jump-to-definition
- Performance: No grammar compilation step speeds up CI/CD
- Debugging: Standard TypeScript debugging tools work directly on grammar code
- Incremental Parsing: Chevrotain supports re-parsing subsets of input (LSP-friendly)
Negative
- Learning Curve: Team must learn Chevrotain's combinator API (not traditional BNF)
- Grammar Verbosity: Code-based grammars are more verbose than declarative formats
- Tooling Gap: No grammar visualization tools (unlike ANTLR4's railroad diagrams)
Neutral
- Grammar Migration: Lark grammar must be manually ported to Chevrotain rules
- Testing: Grammar tests become TypeScript unit tests (Vitest-compatible)
Performance Targets
- Tokenization: <1ms for 4KB template
- Parsing: <5ms for 4KB template with 100 tokens
- Error Recovery: <10ms for malformed 4KB template