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