1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
//! 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""#));
//! ```
//!
//! ## Names and string values: from atom to `&str`
//!
//! Text in the AST is *interned*: `id.name` is a `Cell<NodeLabel>`, an index
//! into the arena's atom table, not a `String`. Every text field has a
//! generated accessor that does the lookup against the [`ast::context::GCLock`],
//! returning a `&str` that borrows the table's bytes — no allocation:
//!
//! ```
//! use hermes_parser::ast::node::Node;
//! use hermes_parser::{parse, ParseFlags};
//!
//! let src = r#"function greet(who) { return "hi"; }"#;
//! let mut parsed = parse(src, ParseFlags::default()).expect("parse error");
//!
//! let names = parsed.with_program(|gc, program| {
//! let Node::Program(p) = program else { unreachable!() };
//! let Some(Node::FunctionDeclaration(f)) = p.body.iter().next()
//! else { unreachable!() };
//! let Some(Node::Identifier(id)) = f.id else { unreachable!() };
//! let Some(Node::Identifier(param)) = f.params.iter().next()
//! else { unreachable!() };
//! // `<field>_str` for a name-like field…
//! (id.name_str(gc).to_string(), param.name_str(gc).to_string())
//! });
//! assert_eq!(names, ("greet".to_string(), "who".to_string()));
//!
//! let mut parsed = parse(r#""hi";"#, ParseFlags::default()).expect("parse");
//! let value = parsed.with_program(|gc, program| {
//! let Node::Program(p) = program else { unreachable!() };
//! let Some(Node::ExpressionStatement(st)) = p.body.iter().next()
//! else { unreachable!() };
//! let Node::StringLiteral(s) = st.expression else { unreachable!() };
//! // …but `try_<field>_str` for a string *value*, which can legally be an
//! // unpaired surrogate and then has no UTF-8 form at all.
//! s.try_value_str(gc).map(str::to_string)
//! });
//! assert_eq!(value.as_deref(), Some("hi"));
//! ```
//!
//! The split is deliberate. Name-like fields (identifiers, operators,
//! keyword-like kinds) get a plain `<field>_str` that substitutes U+FFFD in
//! the case that should not arise — the lexer rejects an identifier containing
//! an unpaired surrogate. String-literal values get `try_<field>_str`
//! returning `Option<&str>` and an explicit `<field>_str_lossy`, because a
//! lone surrogate there is a legal JS value, and silently replacing it would
//! corrupt the program a codegen tool round-trips. An astral character such as
//! `"😀"` is *not* the `None` case: it is stored as a WTF-8 surrogate pair and
//! both accessors fold it back into the character. For the exact stored bytes,
//! use [`ast::context::GCLock::bytes`]; [`ast::context::GCLock::bytes_str_lossy`]
//! and [`ast::context::GCLock::try_bytes_str`] are the same conversions for an
//! atom you already hold.
//!
//! `crates/sema/examples/print_bindings.rs` puts this together with a
//! `Visitor` walk and name resolution.
//!
//! The pieces a consumer touches:
//! - [`parse`] / [`parse_named`] returning [`ParsedJS`] — the convenience
//! façade, which assembles an [`hermes_ast::context::Context`], a
//! `SourceErrorManager`, a [`lexer::JSLexer`] and a [`js::JSParserImpl`]
//! into 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 name [`hermes_ast::node::Node`], walk with
//! [`hermes_ast::visitor::Visitor`], or drive [`hermes_ast::dump`] by hand.
//! - [`js::JSParserImpl`] — the recursive-descent parser; `new` + `parse`
//! returns the `Program` node, or `None` after a reported error.
//! - [`lexer::JSLexer`] — the lexer, usable on its own; it reports through a
//! `hermes_support::manager::SourceErrorManager` and interns into an `AtomTable`.
//! - [`token::Token`] and [`token_kinds::TokenKind`] — the token surface, the
//! latter generated from `include/hermes/Parser/TokenKinds.def` order.
//! - [`js::ParserPass`] — `FullParse` (eager), plus the `PreParse`/`LazyParse`
//! pair that indexes function bodies in one scan and defers parsing them.
//! - [`json`] — the separate `JSONParser` port (a distinct grammar sharing the
//! same lexer), with the uniquing/hidden-class `JSONFactory`.
//!
//! 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.
/// The façade module is private: its items are re-exported here so each has
/// exactly one path in the docs.
pub use ;
/// The AST crate, re-exported under the short name `ast`, so the public path
/// is `hermes_parser::ast`. Parsing hands back AST types, so a consumer needs
/// them; re-exporting keeps this crate the only dependency they must declare.
/// `ast::node::Node`, `ast::visitor`, `ast::context::GCLock` and `ast::dump`
/// are the pieces the façade's signatures mention. The same items are also
/// reachable as `hermes_ast::…` by depending on that crate directly.
pub use hermes_ast as ast;
/// One recorded diagnostic, re-exported because it appears in the façade's
/// signatures ([`ParseError::diagnostics`], [`ParsedJS::diagnostics`]).
/// Render one with `hermes_support::render::render_diagnostic`.
pub use ResolvedDiagnostic;