1use std::collections::{HashMap, HashSet};
9use std::sync::Arc;
10
11use crate::grammar::inference::eval::MembershipOracle;
12use crate::grammar::{CharClassItem, Grammar, GrammarExpr};
13use crate::{
14 ByteRange, LanguageParser, LinkFlags, LinkId, LinkMetadata, LinkNetwork, LinkType,
15 ParseConfiguration, ParserRegistry, Point, SourceSpan,
16};
17
18const EXPR_EMPTY: &str = "grammar::runtime::expr::empty";
19const EXPR_TERMINAL: &str = "grammar::runtime::expr::terminal";
20const EXPR_TERMINAL_INSENSITIVE: &str = "grammar::runtime::expr::terminal-insensitive";
21const EXPR_CHAR_RANGE: &str = "grammar::runtime::expr::char-range";
22const EXPR_CHAR_CLASS: &str = "grammar::runtime::expr::char-class";
23const EXPR_ANY_CHAR: &str = "grammar::runtime::expr::any-char";
24const EXPR_CHOICE: &str = "grammar::runtime::expr::choice";
25const EXPR_SEQUENCE: &str = "grammar::runtime::expr::sequence";
26const EXPR_OPTIONAL: &str = "grammar::runtime::expr::optional";
27const EXPR_ZERO_OR_MORE: &str = "grammar::runtime::expr::zero-or-more";
28const EXPR_ONE_OR_MORE: &str = "grammar::runtime::expr::one-or-more";
29const EXPR_REPEAT: &str = "grammar::runtime::expr::repeat";
30const EXPR_AND: &str = "grammar::runtime::expr::and";
31const EXPR_NOT: &str = "grammar::runtime::expr::not";
32
33#[derive(Clone, Debug)]
41pub struct GrammarParser {
42 grammar: Grammar,
43 diagnostics: Vec<String>,
44}
45
46impl GrammarParser {
47 #[must_use]
53 pub fn new(grammar: Grammar) -> Self {
54 let diagnostics = validate_grammar(&grammar);
55 Self {
56 grammar,
57 diagnostics,
58 }
59 }
60
61 #[must_use]
63 pub const fn grammar(&self) -> &Grammar {
64 &self.grammar
65 }
66
67 #[must_use]
69 pub fn diagnostics(&self) -> &[String] {
70 &self.diagnostics
71 }
72
73 #[must_use]
78 pub fn accepts(&self, text: &str) -> bool {
79 self.parse_full(text).is_some()
80 }
81
82 fn parse_full(&self, text: &str) -> Option<ParseNode> {
83 if !self.diagnostics.is_empty() {
84 return None;
85 }
86
87 RuntimeMatcher::new(&self.grammar, text)
88 .parse_full()
89 .ok()
90 .flatten()
91 }
92
93 fn try_parse_network(
94 &self,
95 text: &str,
96 language: &str,
97 configuration: ParseConfiguration,
98 ) -> Option<LinkNetwork> {
99 let tree = self.parse_full(text)?;
100 let (mut network, document) = LinkNetwork::new_parse_document(text, language);
101 let context = EmitContext {
102 text,
103 language,
104 configuration,
105 };
106 emit_parse_node(&mut network, document, &tree, context);
107 network.attach_embedded_regions(document, text, language, configuration);
108 Some(network)
109 }
110}
111
112impl LanguageParser for GrammarParser {
113 fn parse_source(
114 &self,
115 text: &str,
116 language: &str,
117 configuration: ParseConfiguration,
118 ) -> LinkNetwork {
119 self.try_parse_network(text, language, configuration)
120 .unwrap_or_else(|| LinkNetwork::parse_lossless_text(text, language, configuration))
121 }
122}
123
124impl MembershipOracle for GrammarParser {
125 fn accepts(&self, text: &str) -> bool {
126 Self::accepts(self, text)
127 }
128}
129
130pub fn register_grammar(
132 registry: &mut ParserRegistry,
133 key: impl Into<String>,
134 grammar: Grammar,
135) -> &mut ParserRegistry {
136 registry.register(key, Arc::new(GrammarParser::new(grammar)))
137}
138
139#[must_use]
141pub fn with_grammar(
142 registry: ParserRegistry,
143 key: impl Into<String>,
144 grammar: Grammar,
145) -> ParserRegistry {
146 registry.with_parser(key, Arc::new(GrammarParser::new(grammar)))
147}
148
149fn validate_grammar(grammar: &Grammar) -> Vec<String> {
150 let mut diagnostics = Vec::new();
151 if grammar.rules().is_empty() {
152 diagnostics.push("grammar has no start rule".to_string());
153 }
154 if let Some(start) = grammar.start() {
155 if grammar.rule(start).is_none() {
156 diagnostics.push(format!("start rule `{start}` is not defined"));
157 }
158 }
159 diagnostics.extend(
160 grammar
161 .undefined_nonterminals()
162 .into_iter()
163 .map(|rule| format!("undefined non-terminal `{rule}`")),
164 );
165 diagnostics
166}
167
168#[derive(Clone, Debug, PartialEq, Eq)]
169struct RuntimeMatcher<'grammar, 'text> {
170 grammar: &'grammar Grammar,
171 text: &'text str,
172 memo: HashMap<(String, usize), Option<ParseNode>>,
173 active: HashSet<(String, usize)>,
174}
175
176impl<'grammar, 'text> RuntimeMatcher<'grammar, 'text> {
177 fn new(grammar: &'grammar Grammar, text: &'text str) -> Self {
178 Self {
179 grammar,
180 text,
181 memo: HashMap::new(),
182 active: HashSet::new(),
183 }
184 }
185
186 fn parse_full(&mut self) -> Result<Option<ParseNode>, MatchError> {
187 let Some(start) = self.grammar.start_rule() else {
188 return Ok(None);
189 };
190 Ok(self
191 .match_rule(start.name(), 0)?
192 .filter(|node| node.end == self.text.len()))
193 }
194
195 fn match_rule(&mut self, name: &str, position: usize) -> Result<Option<ParseNode>, MatchError> {
196 if !self.valid_position(position) {
197 return Ok(None);
198 }
199
200 let key = (name.to_string(), position);
201 if let Some(cached) = self.memo.get(&key) {
202 return Ok(cached.clone());
203 }
204 if self.active.contains(&key) {
205 return Err(MatchError::LeftRecursive);
206 }
207
208 let Some(rule) = self.grammar.rule(name) else {
209 return Ok(None);
210 };
211
212 self.active.insert(key.clone());
213 let result = self.match_expr(rule.expr(), position);
214 self.active.remove(&key);
215
216 let child = result?;
217 let node = child
218 .map(|child| ParseNode::structural(rule_term(name), position, child.end, vec![child]));
219 self.memo.insert(key, node.clone());
220 Ok(node)
221 }
222
223 fn match_expr(
224 &mut self,
225 expr: &GrammarExpr,
226 position: usize,
227 ) -> Result<Option<ParseNode>, MatchError> {
228 if !self.valid_position(position) {
229 return Ok(None);
230 }
231
232 match expr {
233 GrammarExpr::Empty => Ok(Some(ParseNode::structural(
234 EXPR_EMPTY,
235 position,
236 position,
237 Vec::new(),
238 ))),
239 GrammarExpr::Terminal(value) => {
240 Ok(self.match_terminal(EXPR_TERMINAL, value, position, false))
241 }
242 GrammarExpr::TerminalInsensitive(value) => {
243 Ok(self.match_terminal(EXPR_TERMINAL_INSENSITIVE, value, position, true))
244 }
245 GrammarExpr::CharRange(start, end) => Ok(self
246 .char_at(position)
247 .filter(|(value, _next)| start <= value && value <= end)
248 .map(|(_value, next)| ParseNode::token(EXPR_CHAR_RANGE, position, next))),
249 GrammarExpr::CharClass { negated, items } => Ok(self
250 .char_at(position)
251 .filter(|(value, _next)| class_accepts(*value, *negated, items))
252 .map(|(_value, next)| ParseNode::token(EXPR_CHAR_CLASS, position, next))),
253 GrammarExpr::AnyChar => Ok(self
254 .char_at(position)
255 .map(|(_value, next)| ParseNode::token(EXPR_ANY_CHAR, position, next))),
256 GrammarExpr::NonTerminal(name) => {
257 let child = self.match_rule(name, position)?;
258 Ok(child.map(|child| {
259 ParseNode::structural(non_terminal_term(name), position, child.end, vec![child])
260 }))
261 }
262 GrammarExpr::Choice {
263 ordered,
264 alternatives,
265 } => self.match_choice(*ordered, alternatives, position),
266 GrammarExpr::Sequence(items) => self.match_sequence(items, position),
267 GrammarExpr::Optional(inner) => {
268 if let Some(child) = self.match_expr(inner, position)? {
269 Ok(Some(ParseNode::structural(
270 EXPR_OPTIONAL,
271 position,
272 child.end,
273 vec![child],
274 )))
275 } else {
276 Ok(Some(ParseNode::structural(
277 EXPR_OPTIONAL,
278 position,
279 position,
280 Vec::new(),
281 )))
282 }
283 }
284 GrammarExpr::ZeroOrMore(inner) => {
285 self.match_repetition(EXPR_ZERO_OR_MORE, inner, position, 0, None)
286 }
287 GrammarExpr::OneOrMore(inner) => {
288 self.match_repetition(EXPR_ONE_OR_MORE, inner, position, 1, None)
289 }
290 GrammarExpr::Repeat { expr, min, max } => {
291 self.match_repetition(EXPR_REPEAT, expr, position, *min, *max)
292 }
293 GrammarExpr::And(inner) => {
294 if self.match_expr(inner, position)?.is_some() {
295 Ok(Some(ParseNode::structural(
296 EXPR_AND,
297 position,
298 position,
299 Vec::new(),
300 )))
301 } else {
302 Ok(None)
303 }
304 }
305 GrammarExpr::Not(inner) => {
306 if self.match_expr(inner, position)?.is_none() {
307 Ok(Some(ParseNode::structural(
308 EXPR_NOT,
309 position,
310 position,
311 Vec::new(),
312 )))
313 } else {
314 Ok(None)
315 }
316 }
317 GrammarExpr::Capture { label, expr } => {
318 let child = self.match_expr(expr, position)?;
319 Ok(child.map(|child| {
320 ParseNode::structural(
321 capture_term(label.as_deref()),
322 position,
323 child.end,
324 vec![child],
325 )
326 }))
327 }
328 }
329 }
330
331 fn match_choice(
332 &mut self,
333 ordered: bool,
334 alternatives: &[GrammarExpr],
335 position: usize,
336 ) -> Result<Option<ParseNode>, MatchError> {
337 if ordered {
338 for alternative in alternatives {
339 if let Some(child) = self.match_expr(alternative, position)? {
340 return Ok(Some(ParseNode::structural(
341 EXPR_CHOICE,
342 position,
343 child.end,
344 vec![child],
345 )));
346 }
347 }
348 return Ok(None);
349 }
350
351 let mut best: Option<ParseNode> = None;
352 for alternative in alternatives {
353 let candidate = self.match_expr(alternative, position)?;
354 let replace_best = candidate
355 .as_ref()
356 .is_some_and(|node| best.as_ref().map_or(true, |best| node.end > best.end));
357 if replace_best {
358 best = candidate;
359 }
360 }
361
362 Ok(best.map(|child| ParseNode::structural(EXPR_CHOICE, position, child.end, vec![child])))
363 }
364
365 fn match_sequence(
366 &mut self,
367 items: &[GrammarExpr],
368 position: usize,
369 ) -> Result<Option<ParseNode>, MatchError> {
370 let mut current = position;
371 let mut children = Vec::with_capacity(items.len());
372 for item in items {
373 let Some(child) = self.match_expr(item, current)? else {
374 return Ok(None);
375 };
376 current = child.end;
377 children.push(child);
378 }
379
380 Ok(Some(ParseNode::structural(
381 EXPR_SEQUENCE,
382 position,
383 current,
384 children,
385 )))
386 }
387
388 fn match_repetition(
389 &mut self,
390 term: &'static str,
391 inner: &GrammarExpr,
392 position: usize,
393 min: usize,
394 max: Option<usize>,
395 ) -> Result<Option<ParseNode>, MatchError> {
396 if max.is_some_and(|max| max < min) {
397 return Ok(None);
398 }
399
400 let mut current = position;
401 let mut count = 0;
402 let mut children = Vec::new();
403 loop {
404 if max.is_some_and(|max| count >= max) {
405 break;
406 }
407
408 let Some(child) = self.match_expr(inner, current)? else {
409 break;
410 };
411
412 if child.end == current {
413 if count < min {
414 count = min;
415 children.push(child);
416 }
417 break;
418 }
419
420 current = child.end;
421 count += 1;
422 children.push(child);
423 }
424
425 if count >= min {
426 Ok(Some(ParseNode::structural(
427 term, position, current, children,
428 )))
429 } else {
430 Ok(None)
431 }
432 }
433
434 fn match_terminal(
435 &self,
436 term: &'static str,
437 value: &str,
438 position: usize,
439 insensitive: bool,
440 ) -> Option<ParseNode> {
441 let matches = if insensitive {
442 starts_with_ascii_insensitive(&self.text[position..], value)
443 } else {
444 self.text[position..].starts_with(value)
445 };
446 matches.then(|| ParseNode::token(term, position, position + value.len()))
447 }
448
449 fn char_at(&self, position: usize) -> Option<(char, usize)> {
450 self.text[position..]
451 .chars()
452 .next()
453 .map(|value| (value, position + value.len_utf8()))
454 }
455
456 fn valid_position(&self, position: usize) -> bool {
457 position <= self.text.len() && self.text.is_char_boundary(position)
458 }
459}
460
461#[derive(Clone, Debug, PartialEq, Eq)]
462enum MatchError {
463 LeftRecursive,
464}
465
466#[derive(Clone, Debug, PartialEq, Eq)]
467struct ParseNode {
468 term: String,
469 start: usize,
470 end: usize,
471 children: Vec<Self>,
472 token: bool,
473}
474
475impl ParseNode {
476 fn structural(term: impl Into<String>, start: usize, end: usize, children: Vec<Self>) -> Self {
477 Self {
478 term: term.into(),
479 start,
480 end,
481 children,
482 token: false,
483 }
484 }
485
486 fn token(term: impl Into<String>, start: usize, end: usize) -> Self {
487 Self {
488 term: term.into(),
489 start,
490 end,
491 children: Vec::new(),
492 token: true,
493 }
494 }
495}
496
497#[derive(Clone, Copy, Debug)]
498struct EmitContext<'source> {
499 text: &'source str,
500 language: &'source str,
501 configuration: ParseConfiguration,
502}
503
504fn emit_parse_node(
505 network: &mut LinkNetwork,
506 owner: LinkId,
507 node: &ParseNode,
508 context: EmitContext<'_>,
509) -> LinkId {
510 let span = span_for_range(context.text, node.start, node.end);
511 let node_id = network.insert_link(
512 [owner],
513 LinkMetadata::new()
514 .with_link_type(LinkType::Grammar)
515 .with_named(true)
516 .with_term(&node.term)
517 .with_language(context.language)
518 .with_span(span)
519 .with_flags(LinkFlags::clean()),
520 );
521
522 for child in &node.children {
523 emit_parse_node(network, node_id, child, context);
524 }
525
526 if node.token && node.start < node.end {
527 emit_token(network, node_id, node.start, node.end, context);
528 }
529
530 node_id
531}
532
533fn emit_token(
534 network: &mut LinkNetwork,
535 owner: LinkId,
536 start: usize,
537 end: usize,
538 context: EmitContext<'_>,
539) -> LinkId {
540 let text = &context.text[start..end];
541 let span = span_for_range(context.text, start, end);
542 let flags = if text.chars().all(char::is_whitespace) {
543 LinkFlags::extra()
544 } else {
545 LinkFlags::clean()
546 };
547 let token = network.insert_link(
548 [owner],
549 LinkMetadata::new()
550 .with_link_type(LinkType::Token)
551 .with_named(!text.chars().all(char::is_whitespace))
552 .with_term(text)
553 .with_language(context.language)
554 .with_span(span)
555 .with_flags(flags),
556 );
557
558 if flags.is_extra() {
559 network.attach_trivia(
560 owner,
561 token,
562 span,
563 context.configuration.trivia_attachment_policy(),
564 );
565 }
566
567 token
568}
569
570fn span_for_range(text: &str, start: usize, end: usize) -> SourceSpan {
571 SourceSpan::new(
572 ByteRange::new(start, end),
573 point_at_byte(text, start),
574 point_at_byte(text, end),
575 )
576}
577
578fn point_at_byte(text: &str, byte: usize) -> Point {
579 let mut row = 0;
580 let mut line_start = 0;
581 for (index, value) in text.bytes().enumerate().take(byte) {
582 if value == b'\n' {
583 row += 1;
584 line_start = index + 1;
585 }
586 }
587 Point::new(row, byte - line_start)
588}
589
590fn starts_with_ascii_insensitive(text: &str, value: &str) -> bool {
591 text.get(..value.len())
592 .is_some_and(|prefix| prefix.eq_ignore_ascii_case(value))
593}
594
595fn class_accepts(value: char, negated: bool, items: &[CharClassItem]) -> bool {
596 let contains = items.iter().any(|item| match item {
597 CharClassItem::Char(item) => *item == value,
598 CharClassItem::Range(start, end) => *start <= value && value <= *end,
599 });
600 contains != negated
601}
602
603fn rule_term(name: &str) -> String {
604 format!("grammar::runtime::rule::{name}")
605}
606
607fn non_terminal_term(name: &str) -> String {
608 format!("grammar::runtime::expr::non-terminal::{name}")
609}
610
611fn capture_term(label: Option<&str>) -> String {
612 label.map_or_else(
613 || "grammar::runtime::capture".to_string(),
614 |label| format!("grammar::runtime::capture::{label}"),
615 )
616}