1use oxc_allocator::{Allocator, ArenaBox, ArenaVec, GetAllocator};
24use oxc_ast::ast::*;
25use oxc_diagnostics::OxcDiagnostic;
26use oxc_span::Span;
27
28use crate::{
29 Context, Parser, ParserConfig as Config, ParserImpl, ParserReturn, StatementContext,
30 config::NoTokensParserConfig,
31 diagnostics::{self, ParserDiagnostic},
32 js::{VariableDeclarationParent, grammar::CoverGrammar},
33 lexer::{Kind, Token},
34};
35
36#[derive(Debug, Clone, Copy, PartialEq, Eq)]
38pub enum MarkupSite {
39 Expression,
41 FunctionBody,
43}
44
45pub trait MarkupGrammar<'a> {
50 fn opens(&self, source: &'a str, offset: u32, site: MarkupSite) -> bool;
53
54 fn region(&self, cursor: &mut MarkupCursor<'_, 'a>, site: MarkupSite) -> u32;
57}
58
59pub struct MarkupCursor<'p, 'a> {
61 host: &'p mut dyn MarkupHost<'a>,
62}
63
64trait MarkupHost<'a> {
65 fn source(&self) -> &'a str;
66 fn allocator(&self) -> &'a Allocator;
67 fn token(&self) -> Token;
68 fn seat(&mut self, offset: u32) -> bool;
69 fn bump(&mut self);
70 fn expression(&mut self) -> Expression<'a>;
71 fn sequence(&mut self) -> Expression<'a>;
72 fn statement(&mut self) -> Statement<'a>;
73 fn for_of_head(&mut self) -> Option<(ForStatementLeft<'a>, Expression<'a>)>;
74 fn binding_identifier(&mut self) -> BindingIdentifier<'a>;
75 fn formal_parameters(&mut self) -> ArenaBox<'a, FormalParameters<'a>>;
76 fn fatal(&mut self, diagnostic: OxcDiagnostic);
77 fn failed(&self) -> bool;
78}
79
80impl<'a> MarkupCursor<'_, 'a> {
81 pub fn source(&self) -> &'a str {
83 self.host.source()
84 }
85
86 pub fn allocator(&self) -> &'a Allocator {
88 self.host.allocator()
89 }
90
91 pub fn token(&self) -> Token {
93 self.host.token()
94 }
95
96 pub fn seat(&mut self, offset: u32) -> bool {
100 self.host.seat(offset)
101 }
102
103 pub fn bump(&mut self) {
105 self.host.bump();
106 }
107
108 pub fn expression(&mut self) -> Expression<'a> {
110 self.host.expression()
111 }
112
113 pub fn sequence(&mut self) -> Expression<'a> {
115 self.host.sequence()
116 }
117
118 pub fn statement(&mut self) -> Statement<'a> {
120 self.host.statement()
121 }
122
123 pub fn for_of_head(&mut self) -> Option<(ForStatementLeft<'a>, Expression<'a>)> {
127 self.host.for_of_head()
128 }
129
130 pub fn binding_identifier(&mut self) -> BindingIdentifier<'a> {
132 self.host.binding_identifier()
133 }
134
135 pub fn formal_parameters(&mut self) -> ArenaBox<'a, FormalParameters<'a>> {
137 self.host.formal_parameters()
138 }
139
140 pub fn fatal(&mut self, diagnostic: OxcDiagnostic) {
142 self.host.fatal(diagnostic);
143 }
144
145 pub fn failed(&self) -> bool {
147 self.host.failed()
148 }
149}
150
151impl<'a, C: Config> MarkupHost<'a> for ParserImpl<'a, C> {
152 fn source(&self) -> &'a str {
153 self.source_text
154 }
155
156 fn allocator(&self) -> &'a Allocator {
157 GetAllocator::allocator(self)
158 }
159
160 fn token(&self) -> Token {
161 self.token
162 }
163
164 fn seat(&mut self, offset: u32) -> bool {
165 self.seat_at(offset)
166 }
167
168 fn bump(&mut self) {
169 self.bump_any();
170 }
171
172 fn expression(&mut self) -> Expression<'a> {
173 self.context_add(Context::In, ParserImpl::parse_assignment_expression_or_higher)
174 }
175
176 fn sequence(&mut self) -> Expression<'a> {
177 self.context_add(Context::In, ParserImpl::parse_expr)
178 }
179
180 fn statement(&mut self) -> Statement<'a> {
181 self.parse_statement_list_item(StatementContext::StatementList)
182 }
183
184 fn for_of_head(&mut self) -> Option<(ForStatementLeft<'a>, Expression<'a>)> {
185 self.parse_markup_for_of_head()
186 }
187
188 fn binding_identifier(&mut self) -> BindingIdentifier<'a> {
189 self.parse_binding_identifier()
190 }
191
192 fn formal_parameters(&mut self) -> ArenaBox<'a, FormalParameters<'a>> {
193 let (_, parameters) = self.parse_formal_parameters(
194 crate::js::FunctionKind::Expression,
195 FormalParameterKind::FormalParameter,
196 );
197 parameters
198 }
199
200 fn fatal(&mut self, diagnostic: OxcDiagnostic) {
201 self.set_fatal_error(ParserDiagnostic::Eager(Box::new(diagnostic)));
202 }
203
204 fn failed(&self) -> bool {
205 self.fatal_error.is_some()
206 }
207}
208
209impl<'a, C: Config> ParserImpl<'a, C> {
210 pub(crate) fn seat_at(&mut self, offset: u32) -> bool {
212 if let Some(token) = self.lexer.seat(offset) {
213 self.prev_token_end = offset;
214 self.token = token;
215 true
216 } else {
217 self.set_fatal_error(diagnostics::unexpected_token(Span::empty(offset)));
218 false
219 }
220 }
221
222 #[inline]
224 pub(crate) fn at_markup_directive(&self, site: MarkupSite) -> bool {
225 C::MARKUP && self.at(Kind::At) && self.markup_opens(site)
226 }
227
228 #[cold]
229 #[inline(never)]
230 fn markup_opens(&self, site: MarkupSite) -> bool {
231 self.markup
232 .is_some_and(|grammar| grammar.opens(self.source_text, self.cur_token().start(), site))
233 }
234
235 #[inline]
237 pub(crate) fn at_markup_line_start_tag(&self) -> bool {
238 C::MARKUP && self.markup_line_start_tag()
239 }
240
241 #[cold]
242 #[inline(never)]
243 fn markup_line_start_tag(&self) -> bool {
244 let token = self.cur_token();
245 token.kind() == Kind::LAngle
246 && token.is_on_new_line()
247 && self.source_text.as_bytes().get(token.end() as usize).is_some_and(|byte| {
248 byte.is_ascii_alphabetic() || matches!(byte, b'_' | b'$' | b'>' | b'{')
249 })
250 }
251
252 fn run_markup(&mut self, site: MarkupSite) -> u32 {
254 let start = self.cur_token().start();
255 let Some(grammar) = self.markup else {
256 self.set_unexpected();
257 return start;
258 };
259 let end = grammar.region(&mut MarkupCursor { host: self }, site);
260 if self.fatal_error.is_none() && (end <= start || !self.seat_at(end)) {
261 self.set_fatal_error(diagnostics::unexpected_token(Span::empty(start)));
262 }
263 end.max(start)
264 }
265
266 #[cold]
268 #[inline(never)]
269 pub(crate) fn parse_markup_expression(&mut self) -> Expression<'a> {
270 let start = self.cur_token().start();
271 let end = self.run_markup(MarkupSite::Expression);
272 let opening = JSXOpeningFragment::new(Span::empty(start), self);
273 let closing = JSXClosingFragment::new(Span::empty(end), self);
274 Expression::JSXFragment(JSXFragment::boxed(
275 Span::new(start, end),
276 opening,
277 ArenaVec::new_in(self),
278 closing,
279 self,
280 ))
281 }
282
283 #[cold]
285 #[inline(never)]
286 pub(crate) fn parse_markup_function_body(&mut self) -> ArenaBox<'a, FunctionBody<'a>> {
287 let start = self.cur_token().start();
288 let end = self.context(Context::Return, Context::TopLevel, |p| {
289 p.run_markup(MarkupSite::FunctionBody)
290 });
291 FunctionBody::boxed(
292 Span::new(start, end),
293 ArenaVec::new_in(self),
294 ArenaVec::new_in(self),
295 self,
296 )
297 }
298
299 fn parse_markup_for_of_head(&mut self) -> Option<(ForStatementLeft<'a>, Expression<'a>)> {
300 self.expect(Kind::LParen);
301 let declaration_kind = match self.cur_kind() {
302 Kind::Const => Some(VariableDeclarationKind::Const),
303 Kind::Let => Some(VariableDeclarationKind::Let),
304 Kind::Var => Some(VariableDeclarationKind::Var),
305 _ => None,
306 };
307 let left = if let Some(kind) = declaration_kind {
308 let start = self.start_span();
309 self.bump_any();
310 let declaration = self.context_remove(Context::In, |p| {
311 p.parse_variable_declaration(start, kind, VariableDeclarationParent::For, false)
312 });
313 ForStatementLeft::VariableDeclaration(declaration)
314 } else {
315 let target =
316 self.context_remove(Context::In, ParserImpl::parse_lhs_expression_or_higher);
317 ForStatementLeft::from(AssignmentTarget::cover(target, self))
318 };
319 if !self.at(Kind::Of) {
320 return None;
321 }
322 self.bump_any();
323 let right =
324 self.context_add(Context::In, ParserImpl::parse_assignment_expression_or_higher);
325 Some((left, right))
326 }
327}
328
329impl<'a> Parser<'a, NoTokensParserConfig> {
330 pub fn parse_with_markup(self, grammar: &'a dyn MarkupGrammar<'a>) -> ParserReturn<'a> {
336 crate::parse_with_markup(
337 self.allocator,
338 self.source_text,
339 self.source_type,
340 self.options,
341 grammar,
342 )
343 }
344}
345
346#[cfg(test)]
347mod tests {
348 use std::cell::RefCell;
349
350 use oxc_allocator::Allocator;
351 use oxc_ast::ast::{Expression, Statement};
352 use oxc_span::{GetSpan, SourceType};
353
354 use super::{MarkupCursor, MarkupGrammar, MarkupSite};
355 use crate::{Kind, Parser};
356
357 type Recorded = Vec<(MarkupSite, u32, u32, String)>;
358
359 #[derive(Default)]
361 struct Toy {
362 regions: RefCell<Recorded>,
363 }
364
365 impl<'a> MarkupGrammar<'a> for Toy {
366 fn opens(&self, source: &'a str, offset: u32, _: MarkupSite) -> bool {
367 source.as_bytes().get(offset as usize + 1) == Some(&b'{')
368 }
369
370 fn region(&self, cursor: &mut MarkupCursor<'_, 'a>, site: MarkupSite) -> u32 {
371 let start = cursor.token().start();
372 cursor.seat(start + 2);
373 let (inner, end) = if cursor.source().as_bytes()[start as usize] == b'<' {
374 let expression = cursor.expression();
375 let close = cursor.token();
376 assert_eq!(close.kind(), Kind::RCurly);
377 (expression.span().source_text(cursor.source()).to_string(), close.end() + 1)
378 } else {
379 let mut statements = Vec::new();
380 while cursor.token().kind() != Kind::RCurly {
381 let statement = cursor.statement();
382 statements.push(statement.span().source_text(cursor.source()).to_string());
383 }
384 (statements.join("|"), cursor.token().end())
385 };
386 self.regions.borrow_mut().push((site, start, end, inner));
387 end
388 }
389 }
390
391 fn parse(source: &str) -> (Recorded, usize, Option<(u32, u32)>) {
392 let toy = Toy::default();
393 let allocator = Allocator::default();
394 let parsed = Parser::new(&allocator, source, SourceType::tsx()).parse_with_markup(&toy);
395 assert!(parsed.diagnostics.is_empty(), "{source}: {:?}", parsed.diagnostics);
396 let placeholder = match parsed.program.body.first() {
397 Some(Statement::VariableDeclaration(declaration)) => {
398 match declaration.declarations[0].init.as_ref() {
399 Some(Expression::JSXFragment(fragment)) => {
400 assert!(fragment.children.is_empty());
401 Some((fragment.span.start, fragment.span.end))
402 }
403 _ => None,
404 }
405 }
406 Some(Statement::FunctionDeclaration(function)) => {
407 let body = function.body.as_ref().unwrap();
408 assert!(body.statements.is_empty());
409 Some((body.span.start, body.span.end))
410 }
411 _ => None,
412 };
413 let regions = toy.regions.borrow().clone();
414 (regions, parsed.program.body.len(), placeholder)
415 }
416
417 #[test]
418 fn regions_keep_exact_placeholders_and_reenter_the_host() {
419 let (regions, statements, placeholder) = parse("const a = <{b + [<{c}>][0]}>;");
420 assert_eq!(statements, 1);
421 assert_eq!(placeholder, Some((10, 28)));
422 assert_eq!(
423 regions,
424 [
425 (MarkupSite::Expression, 17, 22, "c".to_string()),
426 (MarkupSite::Expression, 10, 28, "b + [<{c}>][0]".to_string()),
427 ]
428 );
429 }
430
431 #[test]
432 fn function_bodies_and_statement_starts_open_regions() {
433 let (regions, _, placeholder) = parse("function f() @{ const x = 1; g(x) }");
434 assert_eq!(placeholder, Some((13, 35)));
435 assert_eq!(regions, [(MarkupSite::FunctionBody, 13, 35, "const x = 1;|g(x)".to_string())]);
436 let (regions, statements, _) = parse("let v;\n@{ v = 1 }\n@dec class K {}");
437 assert_eq!(statements, 3);
438 assert_eq!(regions, [(MarkupSite::Expression, 7, 17, "v = 1".to_string())]);
439 }
440
441 #[test]
442 fn a_line_start_tag_ends_the_previous_expression() {
443 let (regions, statements, _) = parse("const a = b\n<{c}>\nconst d = e < f;");
444 assert_eq!(statements, 3);
445 assert_eq!(regions, [(MarkupSite::Expression, 12, 17, "c".to_string())]);
446 }
447}