Skip to content

Parsing Architecture & Tree-sitter Usage ​

HTTPX parses two fundamentally different kinds of input with two different engines. This page documents where each lives and why.

Tree-sitter usage ​

Tree-sitter is used where a real grammar beats a hand-written scanner. Each parsing module imports the treesitter dependency directly, because there is no shared wrapper to route through:

  • src/parsing/html.zig - HTML grammar, plus incremental reparse
  • src/parsing/xml.zig - XML grammar
  • src/parsing/document.zig - incremental edits, edits and queries over whichever grammar the content kind selects
  • src/web/templates/templateParser.zig - the template grammar

Tree-sitter is intentionally hidden from the public API: user code only needs @import("httpx").

Used by:

  • HTML/document parsing - source positions for incremental updates
  • Template parsing - the full Jinja language surface

Not used for:

  • HTTP wire parsing
  • HTTP/2 frames
  • HTTP/3 frames and QPACK
  • QUIC
  • TLS
  • DNS wire format
  • WebSocket frames
  • FTP wire protocol
  • JSON and JSON Feed, which use std.json
  • Robots.txt and sitemaps, which use their own scanners
  • The file watcher, which compares stat results and never parses content
  • compression
  • other binary/protocol parsing

Why native parsers remain ​

The installed treesitter-0.0.1 dependency ships exactly four grammars: arithmetic expressions, s-expressions, JSON, and an indentation outline demo, so HTTPX defines first-class HTML, XML, and template grammars on top of the Tree-sitter runtime: htmlLanguage in src/parsing/html.zig, xmlLanguage in src/parsing/xml.zig, and templateLanguage in src/web/templates/templateParser.zig. Syntax tokens and ranges come from the syntax tree, while tag matching, DOM building, and template nesting/AST semantics stay in HTTPX. JSON value decoding stays on std.json; structural consumers are JSON Feed and templates (syntax tree + positions from the grammar, field/AST mapping in HTTPX). Tiny line-oriented formats (robots.txt, sitemap) and small recursive-descent languages (GraphQL, route patterns, selectors) are objectively simpler, faster, and lower-allocation as specialized native parsers.

Grammar inventory ​

GrammarShips in depHTTPX consumerReason
Arithmetic expressionsYes (demo)NoneDemo grammar; template expressions are paths/filters, not arithmetic
S-expressionsYes (demo)NoneNo s-expression input in HTTPX
JSONYesFeed (JSON Feed syntax layer)Only shipped grammar with an HTTPX consumer; value decoding stays std.json
Outline (indent demo)Yes (demo)NoneDemo only
HTMLNo—Native parser retained
Templates ({{ }}, {% %}, {# #})HTTPX-defined (templateLanguage)Templates (syntax layer)Tree-sitter tokenizes; nesting/AST stays in HTTPX
CSS selectorsNo—Native parser retained
GraphQLNo—Native spec-driven parser retained
OpenAPI/YAMLNo—Generation from router metadata + std.json; retained

Parser decision matrix ​

ComponentCurrent parserTree-sitterDecisionReason
TemplatesTree-sitter syntax tokens + HTTPX AST (web/templates/parser.zig)YesIntegratedGrammar tokenizes text/expression/directive/comment; nesting stays native
HTMLnative tokenizer + DOM (parsing/html.zig, dom.zig)No grammarKeep nativeNo HTML grammar ships; streaming + arena DOM fits serving
DOMbuilt from native HTML parseNoKeep nativeSingle representation already; no second tree
Documentnative HTML + computeEdit vocabularyVocabulary onlyKeep nativeEdit descriptors shared with watcher; no grammar to parse with
Selectorsnative CSS-selector parser (parsing/selector.zig)NoKeep nativeTiny grammar; dedicated parser is faster/simpler
ExtractionDOM traversal (parsing/extract.zig)NoKeep nativeOperates on the single DOM; no reparse
JSONstd.json for valuesStructural for feedsValues decode via std; feed documents parse via the grammar
XMLnative streaming parser (parsing/xml.zig)NoKeep nativeStreaming fits feeds; no XML grammar ships
Feed (RSS/Atom)native over XML/DOMNoKeep nativeThin layer over retained parsers
Feed (JSON)Tree-sitter JSON syntax + HTTPX semanticsYesIntegratedGrammar fits; replaces a stub that ignored its input
Robots.txtnative line parser (parsing/robots.zig)NoKeep nativeLine-oriented; Tree-sitter is overkill
Sitemapnative XML-backed parser (parsing/sitemap.zig)NoKeep nativeSame as XML/feeds
GraphQLnative lexer + recursive descent (web/graphql/)NoKeep nativeSpec-driven with depth/complexity limits; no GraphQL grammar ships
OpenAPIbuilt from router metadata + std.jsonNoKeep nativeGeneration, not parsing; no grammar needed
Router patternsnative segment parser (web/router/pattern.zig)NoKeep nativeTrivial syntax ({param}, *wild); overhead unjustified
URI/URLnative (common/uri.zig)NoKeep nativeNot a Tree-sitter domain
HTTP/1.xnative wire parserNoKeep nativeBinary/protocol parsing
HTTP/2 (frames/HPACK)nativeNoKeep nativeBinary/protocol parsing
HTTP/3/QUICnativeNoKeep nativeBinary/protocol parsing
TLS/DNS/WS/FTPnativeNoKeep nativeBinary/protocol parsing
Multipartnative boundary parserNoKeep nativeWire format parsing
Compressionbrotli/zstd deps + stdNoKeep deps/stdCodec domain, not parsing

Released under the MIT License.