sim_codec_javascript/types.rs
1//! Runtime-independent public syntax and extension data.
2
3use std::fmt;
4
5/// Half-open UTF-8 byte range in the original source.
6#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
7pub struct Span {
8 /// First byte.
9 pub start: usize,
10 /// Byte after the range.
11 pub end: usize,
12}
13
14/// Script or Module syntactic goal.
15#[derive(Clone, Copy, Debug, Eq, PartialEq)]
16pub enum Goal {
17 /// Script goal.
18 Script,
19 /// Module goal (always strict).
20 Module,
21}
22
23/// Parser-selected lexical goal governing slash and template recognition.
24#[derive(Clone, Copy, Debug, Eq, PartialEq)]
25pub enum LexicalGoal {
26 /// Division or division-assignment is admitted.
27 Div,
28 /// A regular-expression literal is admitted.
29 RegExp,
30 /// A template tail follows a substitution.
31 TemplateTail,
32}
33
34/// Kind of lossless token.
35#[derive(Clone, Debug, Eq, PartialEq)]
36pub enum TokenKind {
37 /// IdentifierName, including escaped spelling.
38 Identifier,
39 /// Reserved or contextual keyword spelling.
40 Keyword,
41 /// Numeric literal.
42 Number,
43 /// String literal.
44 String,
45 /// Regular-expression literal.
46 RegExp,
47 /// No-substitution template or template segment.
48 Template,
49 /// Punctuator.
50 Punctuator,
51 /// Whitespace, line terminator, comment, or hashbang.
52 Trivia,
53 /// End marker.
54 End,
55}
56
57/// A lossless lexical token. Text is recovered using [`Token::span`].
58#[derive(Clone, Debug, Eq, PartialEq)]
59pub struct Token {
60 /// Token category.
61 pub kind: TokenKind,
62 /// Original byte range.
63 pub span: Span,
64 /// One-based line.
65 pub line: usize,
66 /// Zero-based Unicode-scalar column.
67 pub column: usize,
68 /// Lexical goal used to recognize this token.
69 pub goal: LexicalGoal,
70}
71
72/// Evidence for an explicit or automatic semicolon boundary.
73#[derive(Clone, Copy, Debug, Eq, PartialEq)]
74pub enum Asi {
75 /// Source contained a semicolon.
76 Explicit(Span),
77 /// A line terminator caused insertion.
78 LineTerminator(Span),
79 /// A closing brace caused insertion.
80 ClosingBrace(Span),
81 /// End of input caused insertion.
82 EndOfInput(Span),
83}
84
85/// Neutral concrete-tree node category, stable for downstream extensions.
86#[derive(Clone, Debug, Eq, PartialEq)]
87#[non_exhaustive]
88pub enum NodeKind {
89 /// Script root.
90 Script,
91 /// Module root.
92 Module,
93 /// Statement list.
94 StatementList,
95 /// A declaration.
96 Declaration,
97 /// A statement.
98 Statement,
99 /// Function declaration or expression.
100 Function,
101 /// Class declaration or expression.
102 Class,
103 /// Import declaration.
104 Import,
105 /// Export declaration.
106 Export,
107 /// Expression region grouped through shared Pratt precedence.
108 Expression,
109 /// Delimited or computed region.
110 Group,
111}
112
113/// A concrete node covering a contiguous token range.
114#[derive(Clone, Debug, Eq, PartialEq)]
115pub struct Node {
116 /// Node category.
117 pub kind: NodeKind,
118 /// Half-open token-index range.
119 pub tokens: std::ops::Range<usize>,
120 /// Nested structural nodes.
121 pub children: Vec<Node>,
122 /// Semicolon boundary, when applicable.
123 pub asi: Option<Asi>,
124}
125
126/// Source identity attachable by JavaScript or downstream syntax extensions.
127#[derive(Clone, Debug, Eq, PartialEq)]
128pub struct Origin {
129 /// Caller-owned source identity.
130 pub source: String,
131 /// Source byte range.
132 pub span: Span,
133 /// Optional parent origin, preserving transformation chains.
134 pub parent: Option<Box<Origin>>,
135}
136
137/// Complete lossless syntax tree.
138#[derive(Clone, Debug, Eq, PartialEq)]
139pub struct SyntaxTree {
140 source: String,
141 /// Selected root goal.
142 pub goal: Goal,
143 /// Lossless tokens, including trivia.
144 pub tokens: Vec<Token>,
145 /// Structural root.
146 pub root: Node,
147}
148impl SyntaxTree {
149 /// Returns the exact admitted input.
150 #[must_use]
151 pub fn source(&self) -> &str {
152 &self.source
153 }
154 /// Re-emits the input byte-for-byte.
155 #[must_use]
156 pub fn preserve_source(&self) -> String {
157 self.source.clone()
158 }
159 pub(crate) fn new(source: &str, goal: Goal, tokens: Vec<Token>, root: Node) -> Self {
160 Self {
161 source: source.to_owned(),
162 goal,
163 tokens,
164 root,
165 }
166 }
167}
168
169/// Resource limits shared by lexing and parsing.
170#[derive(Clone, Copy, Debug, Eq, PartialEq)]
171pub struct Limits {
172 /// Maximum source bytes.
173 pub max_bytes: usize,
174 /// Maximum emitted tokens.
175 pub max_tokens: usize,
176 /// Maximum delimiter/template/tree nesting.
177 pub max_nesting: usize,
178 /// Maximum physical lines.
179 pub max_lines: usize,
180 /// Maximum nodes.
181 pub max_nodes: usize,
182}
183impl Default for Limits {
184 fn default() -> Self {
185 Self {
186 max_bytes: 4 * 1024 * 1024,
187 max_tokens: 1_000_000,
188 max_nesting: 256,
189 max_lines: 250_000,
190 max_nodes: 1_000_000,
191 }
192 }
193}
194
195/// Stable diagnostic category.
196#[derive(Clone, Copy, Debug, Eq, PartialEq)]
197pub enum DiagnosticCode {
198 /// Configured resource bound crossed.
199 ResourceLimit,
200 /// Invalid source character.
201 InvalidCharacter,
202 /// Unterminated literal or comment.
203 UnterminatedLiteral,
204 /// Unmatched or crossed delimiter.
205 UnmatchedDelimiter,
206 /// Grammar violation.
207 InvalidSyntax,
208 /// Static-semantics early error.
209 EarlyError,
210}
211/// Deterministic located frontend failure.
212#[derive(Clone, Debug, Eq, PartialEq)]
213pub struct Diagnostic {
214 /// Stable category.
215 pub code: DiagnosticCode,
216 /// Offending source range.
217 pub span: Span,
218 /// One-based line.
219 pub line: usize,
220 /// Zero-based scalar column.
221 pub column: usize,
222 /// Stable detail.
223 pub message: String,
224}
225impl fmt::Display for Diagnostic {
226 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
227 write!(f, "{}:{}: {}", self.line, self.column, self.message)
228 }
229}
230impl std::error::Error for Diagnostic {}