Expand description
A Rust port of the Hermes JavaScript front end — lexer and parser.
Faithful 1:1 port of the C++ JSLexer and JSParserImpl, validated
byte-for-byte against hermesc -dump-ast over a per-dialect corpus. The
output is the ESTree AST of the ast crate. Every dialect the C++ parser
supports is complete and covered by that differential gate: ECMAScript,
the Flow type grammar, TypeScript, and JSX. The three non-standard ones
are opt-in through the same hermes_ast::context::Context flags as in the C++
(parse_flow and its four extension flags, parse_ts, parse_jsx).
§Quickstart
use hermes_parser::ast::node::Node;
use hermes_parser::{parse, ParseFlags};
let flags = ParseFlags::default();
let mut parsed = parse("1 + 2;", flags).expect("parse error");
// The AST lives in an arena owned by `parsed`; read it under a lock.
let statements = parsed.with_program(|_gc, program| match program {
Node::Program(p) => p.body.iter().count(),
_ => unreachable!("the root of a parse is always a Program"),
});
assert_eq!(statements, 1);
// Or dump it the way `hermesc -dump-ast` does.
let json = parsed.to_estree_json(false);
assert!(json.starts_with(r#"{"type":"Program""#));The pieces a consumer touches:
parse/parse_namedreturningParsedJS— the convenience façade, which assembles anhermes_ast::context::Context, aSourceErrorManager, alexer::JSLexerand ajs::JSParserImplinto one call. It adds no behavior; anything it does not expose is reachable by driving those pieces directly.ast— the AST crate, re-exported so that depending on this crate alone is enough to namehermes_ast::node::Node, walk withhermes_ast::visitor::Visitor, or drivehermes_ast::dumpby hand.js::JSParserImpl— the recursive-descent parser;new+parsereturns theProgramnode, orNoneafter a reported error.lexer::JSLexer— the lexer, usable on its own; it reports through ahermes_support::manager::SourceErrorManagerand interns into anAtomTable.token::Tokenandtoken_kinds::TokenKind— the token surface, the latter generated frominclude/hermes/Parser/TokenKinds.deforder.js::ParserPass—FullParse(eager), plus thePreParse/LazyParsepair that indexes function bodies in one scan and defers parsing them.json— the separateJSONParserport (a distinct grammar sharing the same lexer), with the uniquing/hidden-classJSONFactory.
The remaining modules are the lexer’s own building blocks: cursor (the
scan cursor), number (numeric-literal conversion), utf8 (the
UTF-8/UTF-16 conversions the C++ keeps in Support), and
html_entities (the JSX entity table generated from HTMLEntities.def).
Only the port’s internals call them, and no public signature in this crate
mentions them; they are public incidentally rather than by design, and may
be demoted to pub(crate) in a future release.
See rust/ARCHITECTURE.md for the design rationale and
doc/superpowers/specs/2026-06-06-js-parser-design.md for the port spec.
Re-exports§
pub use hermes_ast as ast;
Modules§
- cursor
- The lexer’s scan cursor. This is the one place the port uses
unsafe(decision “B”): a raw*const u8cursor over the source buffer for parity with the C++ lexer’s pointer arithmetic. The buffer is held as anRc<SourceBuffer>(stable heap address; kept alive for the cursor’s life), and every public method converts to/from a byte offset, so nothingunsafeescapes this module. The buffer is NUL-terminated, sopeek_atone past the last real byte reads the terminating 0 (in-bounds). - html_
entities - XHTML named-entity table for the JSX lexer. Port of the
initializeHTMLEntitiesmap inlib/Parser/JSLexer.cpp, but emitted sorted by name so&name;lookup is a binary search. - js
- The JS parser (
JSParserImpl). Port oflib/Parser/JSParserImpl*. Recursive-descent LL(1) overJSLexer, building theastESTree. - json
- Faithful Rust port of Hermes’ JSONParser (include/hermes/Parser/JSONParser.h,
lib/Parser/JSONParser.cpp): the JSON value model, the uniquing/hidden-class
JSONFactory, and the recursive-descentJSONParseroverJSLexer. - lexer
- JSLexer, a faithful port of
lib/Parser/JSLexer.cpp. - number
- Numeric-literal conversion primitives for the JS lexer, ported from
include/hermes/Support/Conversions.h. The decimal/real path uses Rust std’s
correctly-rounded
str::parse::<f64>()(the same fast_float algorithm the C++ lexer uses) — no FFI, no third-party crate. - token
- Token and friends, ported from include/hermes/Parser/JSLexer.h (Token, RegExpLiteral, StoredComment, StoredToken).
- token_
kinds - Token kinds, ported from include/hermes/Parser/TokenKinds.def.
- utf8
- UTF-8 decode helpers, ported from include/hermes/Support/UTF8.h (decode side).
Structs§
- Parse
Error - A parse that reported at least one error.
- Parse
Flags - Which dialect(s) the parser accepts, plus forced strict mode.
- ParsedJS
- A successful parse: the AST arena, the source manager, and the
Programnode, owned together. - Resolved
Diagnostic - One recorded diagnostic, re-exported because it appears in the façade’s
signatures (
ParseError::diagnostics,ParsedJS::diagnostics). Render one withhermes_support::render::render_diagnostic. A fully resolved diagnostic handed to aDiagHandler. All buffer lookups have already happened, so handlers are free of the source manager.
Functions§
- parse
- Parse
sourceas a script named"input"in diagnostics. - parse_
named - Parse
source, calling itfile_namein diagnostics.