1use std::collections::HashMap;
2use std::fmt;
3
4mod parse;
5
6use crate::link_network::{Link, LinkId, LinkNetwork, LinkType};
7use parse::{QueryParser, tokenize};
8
9#[derive(Clone, Debug, Default, PartialEq, Eq)]
11pub struct LinkQuery {
12 link_type: Option<LinkType>,
13 term: Option<String>,
14 language: Option<String>,
15 named: Option<bool>,
16 pattern: Option<QueryPattern>,
17 pattern_source: Option<String>,
18 predicates: Vec<QueryPredicate>,
19}
20
21impl LinkQuery {
22 #[must_use]
24 pub fn new() -> Self {
25 Self::default()
26 }
27
28 #[must_use]
30 pub fn by_type(link_type: LinkType) -> Self {
31 Self::new().with_link_type(link_type)
32 }
33
34 pub fn from_sexpression(source: &str) -> Result<Self, QueryParseError> {
39 let tokens = tokenize(source)?;
40 let mut parser = QueryParser::new(tokens);
41 let (pattern, predicates) = parser.parse()?;
42 Ok(Self {
43 pattern: Some(pattern),
44 pattern_source: Some(source.to_string()),
45 predicates,
46 ..Self::default()
47 })
48 }
49
50 #[must_use]
52 pub const fn with_link_type(mut self, link_type: LinkType) -> Self {
53 self.link_type = Some(link_type);
54 self
55 }
56
57 #[must_use]
59 pub fn with_term(mut self, term: impl Into<String>) -> Self {
60 self.term = Some(term.into());
61 self
62 }
63
64 #[must_use]
66 pub fn with_language(mut self, language: impl Into<String>) -> Self {
67 self.language = Some(language.into());
68 self
69 }
70
71 #[must_use]
73 pub const fn with_named(mut self, named: bool) -> Self {
74 self.named = Some(named);
75 self
76 }
77
78 pub(crate) fn matches_in_network(
79 &self,
80 network: &LinkNetwork,
81 link: &Link,
82 predicate_host: &impl QueryPredicateHost,
83 ) -> Vec<QueryMatch> {
84 if !self.matches_metadata(link) {
85 return Vec::new();
86 }
87
88 let captures = self.pattern.as_ref().map_or_else(
89 || vec![QueryCaptures::default()],
90 |pattern| pattern.match_root(network, link.id()),
91 );
92
93 captures
94 .into_iter()
95 .filter(|captures| {
96 self.predicates
97 .iter()
98 .all(|predicate| predicate_host.evaluate(predicate, captures, network))
99 })
100 .map(|captures| QueryMatch::new(link.id(), captures))
101 .collect()
102 }
103
104 fn matches_metadata(&self, link: &Link) -> bool {
105 let metadata = link.metadata();
106 self.link_type
107 .is_none_or(|link_type| metadata.link_type() == Some(link_type))
108 && self
109 .term
110 .as_deref()
111 .is_none_or(|term| metadata.term() == Some(term))
112 && self
113 .language
114 .as_deref()
115 .is_none_or(|language| metadata.language() == Some(language))
116 && self.named.is_none_or(|named| metadata.is_named() == named)
117 }
118
119 pub(crate) const fn link_type_filter(&self) -> Option<LinkType> {
120 self.link_type
121 }
122
123 pub(crate) fn term_filter(&self) -> Option<&str> {
124 self.term.as_deref()
125 }
126
127 pub(crate) fn language_filter(&self) -> Option<&str> {
128 self.language.as_deref()
129 }
130
131 pub(crate) const fn named_filter(&self) -> Option<bool> {
132 self.named
133 }
134
135 pub(crate) fn pattern_source(&self) -> Option<&str> {
136 self.pattern_source.as_deref()
137 }
138}
139
140#[derive(Clone, Debug, PartialEq, Eq)]
142pub struct QueryMatch {
143 link_id: LinkId,
144 captures: QueryCaptures,
145}
146
147impl QueryMatch {
148 const fn new(link_id: LinkId, captures: QueryCaptures) -> Self {
149 Self { link_id, captures }
150 }
151
152 #[must_use]
154 pub const fn link_id(&self) -> LinkId {
155 self.link_id
156 }
157
158 #[must_use]
160 pub const fn captures(&self) -> &QueryCaptures {
161 &self.captures
162 }
163}
164
165#[derive(Clone, Debug, Default, PartialEq, Eq)]
167pub struct QueryCaptures {
168 values: Vec<QueryCapture>,
169}
170
171impl QueryCaptures {
172 fn with_capture(mut self, name: &str, link_id: LinkId) -> Self {
173 self.values.push(QueryCapture {
174 name: name.to_string(),
175 link_id,
176 });
177 self
178 }
179
180 #[must_use]
182 pub fn first(&self, name: &str) -> Option<LinkId> {
183 self.values
184 .iter()
185 .find(|capture| capture.name == name)
186 .map(|capture| capture.link_id)
187 }
188
189 pub fn iter(&self) -> impl Iterator<Item = &QueryCapture> {
191 self.values.iter()
192 }
193}
194
195#[derive(Clone, Debug, PartialEq, Eq)]
197pub struct QueryCapture {
198 name: String,
199 link_id: LinkId,
200}
201
202impl QueryCapture {
203 #[must_use]
205 pub fn name(&self) -> &str {
206 &self.name
207 }
208
209 #[must_use]
211 pub const fn link_id(&self) -> LinkId {
212 self.link_id
213 }
214}
215
216pub trait QueryPredicateHost {
218 fn evaluate(
220 &self,
221 predicate: &QueryPredicate,
222 captures: &QueryCaptures,
223 network: &LinkNetwork,
224 ) -> bool;
225}
226
227#[derive(Clone, Copy, Debug, Default)]
230pub struct RejectPredicateHost;
231
232impl QueryPredicateHost for RejectPredicateHost {
233 fn evaluate(
234 &self,
235 _predicate: &QueryPredicate,
236 _captures: &QueryCaptures,
237 _network: &LinkNetwork,
238 ) -> bool {
239 false
240 }
241}
242
243#[must_use]
246pub fn query_by_concept_term(term: &str) -> LinkQuery {
247 LinkQuery::by_type(LinkType::Concept).with_term(term)
248}
249
250#[derive(Clone, Debug)]
258pub struct QueryIndex<'a> {
259 network: &'a LinkNetwork,
260 children: HashMap<LinkId, Vec<LinkId>>,
261 fields: HashMap<LinkId, Vec<(LinkId, LinkId)>>,
262}
263
264impl<'a> QueryIndex<'a> {
265 #[must_use]
267 pub fn new(network: &'a LinkNetwork) -> Self {
268 let link_type = |id: &LinkId| {
269 network
270 .link(*id)
271 .and_then(|link| link.metadata().link_type())
272 };
273 let top_down = network.links().any(|link| {
274 link.metadata().link_type() == Some(LinkType::Syntax)
275 && link
276 .references()
277 .iter()
278 .any(|reference| link_type(reference) == Some(LinkType::Token))
279 });
280 let mut children: HashMap<LinkId, Vec<LinkId>> = HashMap::new();
281 let mut fields: HashMap<LinkId, Vec<(LinkId, LinkId)>> = HashMap::new();
282 for link in network.links() {
283 let references = link.references();
284 let Some(first) = references.first() else {
285 continue;
286 };
287 let kind = link.metadata().link_type();
288 if kind == Some(LinkType::Field) {
289 if let [parent, label, child] = references {
290 fields.entry(*parent).or_default().push((*label, *child));
291 }
292 } else if top_down {
293 if kind == Some(LinkType::Syntax) {
294 for reference in references {
295 if !matches!(
296 link_type(reference),
297 Some(LinkType::Field | LinkType::Trivia)
298 ) {
299 children.entry(link.id()).or_default().push(*reference);
300 }
301 }
302 }
303 } else if kind != Some(LinkType::Trivia) {
304 children.entry(*first).or_default().push(link.id());
305 }
306 }
307 Self {
308 network,
309 children,
310 fields,
311 }
312 }
313
314 #[must_use]
316 pub fn link(&self, id: LinkId) -> Option<&'a Link> {
317 self.network.link(id)
318 }
319
320 #[must_use]
323 pub fn structural_children(&self, parent: LinkId) -> &[LinkId] {
324 self.children.get(&parent).map_or(&[], Vec::as_slice)
325 }
326
327 #[must_use]
329 pub fn field_targets(&self, parent: LinkId, label: &str) -> Vec<LinkId> {
330 self.fields
331 .get(&parent)
332 .into_iter()
333 .flatten()
334 .filter(|(label_id, _)| {
335 self.network
336 .link(*label_id)
337 .and_then(|link| link.metadata().term())
338 == Some(label)
339 })
340 .map(|(_, child)| *child)
341 .collect()
342 }
343}
344
345#[derive(Clone, Debug, PartialEq, Eq)]
347pub struct QueryPredicate {
348 name: String,
349 arguments: Vec<QueryPredicateArgument>,
350}
351
352impl QueryPredicate {
353 #[must_use]
355 pub fn name(&self) -> &str {
356 &self.name
357 }
358
359 #[must_use]
361 pub fn arguments(&self) -> &[QueryPredicateArgument] {
362 &self.arguments
363 }
364}
365
366#[derive(Clone, Debug, PartialEq, Eq)]
368pub enum QueryPredicateArgument {
369 Capture(String),
371 Literal(String),
373}
374
375impl QueryPredicateArgument {
376 #[must_use]
378 pub fn capture_name(&self) -> Option<&str> {
379 match self {
380 Self::Capture(name) => Some(name),
381 Self::Literal(_) => None,
382 }
383 }
384
385 #[must_use]
387 pub fn literal(&self) -> Option<&str> {
388 match self {
389 Self::Capture(_) => None,
390 Self::Literal(value) => Some(value),
391 }
392 }
393}
394
395#[derive(Clone, Debug, PartialEq, Eq)]
397pub struct QueryParseError {
398 message: String,
399}
400
401impl QueryParseError {
402 pub(crate) fn new(message: impl Into<String>) -> Self {
403 Self {
404 message: message.into(),
405 }
406 }
407}
408
409impl fmt::Display for QueryParseError {
410 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
411 formatter.write_str(&self.message)
412 }
413}
414
415impl std::error::Error for QueryParseError {}
416
417#[derive(Clone, Debug, PartialEq, Eq)]
418struct QueryPattern {
419 root: QueryNodePattern,
420 capture: Option<String>,
421}
422
423impl QueryPattern {
424 fn match_root(&self, network: &LinkNetwork, link_id: LinkId) -> Vec<QueryCaptures> {
425 let captures = self
426 .root
427 .matches(network, link_id, QueryCaptures::default());
428 captures
429 .into_iter()
430 .map(|captures| {
431 if let Some(capture) = &self.capture {
432 captures.with_capture(capture, link_id)
433 } else {
434 captures
435 }
436 })
437 .collect()
438 }
439}
440
441#[derive(Clone, Debug, PartialEq, Eq)]
442struct QueryNodePattern {
443 kind: QueryNodeKind,
444 children: Vec<QueryChildPattern>,
445}
446
447impl QueryNodePattern {
448 fn matches(
449 &self,
450 network: &LinkNetwork,
451 link_id: LinkId,
452 captures: QueryCaptures,
453 ) -> Vec<QueryCaptures> {
454 let Some(link) = network.link(link_id) else {
455 return Vec::new();
456 };
457 if !self.kind.matches(link) || self.has_negated_field(network, link_id) {
458 return Vec::new();
459 }
460
461 let structural_children = structural_children(network, link_id);
462 let context = MatchContext {
463 network,
464 parent: link_id,
465 structural_children: &structural_children,
466 };
467 match_child_patterns(&context, &self.children, 0, false, captures)
468 .into_iter()
469 .map(|(_child_index, captures)| captures)
470 .collect()
471 }
472
473 fn has_negated_field(&self, network: &LinkNetwork, link_id: LinkId) -> bool {
474 self.children.iter().any(|child| {
475 if let QueryChildPattern::NegatedField(label) = child {
476 has_field(network, link_id, label)
477 } else {
478 false
479 }
480 })
481 }
482}
483
484#[derive(Clone, Debug, PartialEq, Eq)]
485enum QueryNodeKind {
486 Exact(String),
487 Wildcard,
488}
489
490impl QueryNodeKind {
491 fn matches(&self, link: &Link) -> bool {
492 match self {
493 Self::Exact(kind) => link.metadata().term() == Some(kind.as_str()),
494 Self::Wildcard => true,
495 }
496 }
497}
498
499#[derive(Clone, Debug, PartialEq, Eq)]
500enum QueryChildPattern {
501 Anchor,
502 NegatedField(String),
503 Pattern(QueryChildExpression),
504}
505
506#[derive(Clone, Debug, PartialEq, Eq)]
507struct QueryChildExpression {
508 field: Option<String>,
509 expression: QueryExpression,
510 capture: Option<String>,
511 quantifier: QueryQuantifier,
512}
513
514#[derive(Clone, Debug, PartialEq, Eq)]
515enum QueryExpression {
516 Node(QueryNodePattern),
517 Alternation(Vec<QueryNodePattern>),
518}
519
520#[derive(Clone, Copy, Debug, PartialEq, Eq)]
521enum QueryQuantifier {
522 One,
523 ZeroOrOne,
524 ZeroOrMore,
525 OneOrMore,
526}
527
528struct MatchContext<'a> {
529 network: &'a LinkNetwork,
530 parent: LinkId,
531 structural_children: &'a [LinkId],
532}
533
534fn match_child_patterns(
535 context: &MatchContext<'_>,
536 patterns: &[QueryChildPattern],
537 child_index: usize,
538 anchored: bool,
539 captures: QueryCaptures,
540) -> Vec<(usize, QueryCaptures)> {
541 let Some((pattern, remaining)) = patterns.split_first() else {
542 return vec![(child_index, captures)];
543 };
544
545 match pattern {
546 QueryChildPattern::Anchor => {
547 if remaining.is_empty() || remaining.iter().all(is_negated_field_pattern) {
548 if child_index == context.structural_children.len() {
549 match_child_patterns(context, remaining, child_index, true, captures)
550 } else {
551 Vec::new()
552 }
553 } else {
554 match_child_patterns(context, remaining, child_index, true, captures)
555 }
556 }
557 QueryChildPattern::NegatedField(_) => {
558 match_child_patterns(context, remaining, child_index, anchored, captures)
559 }
560 QueryChildPattern::Pattern(expression) => match_quantified_expression(
561 context,
562 expression,
563 remaining,
564 child_index,
565 anchored,
566 &captures,
567 ),
568 }
569}
570
571const fn is_negated_field_pattern(pattern: &QueryChildPattern) -> bool {
572 matches!(pattern, QueryChildPattern::NegatedField(_))
573}
574
575fn match_quantified_expression(
576 context: &MatchContext<'_>,
577 expression: &QueryChildExpression,
578 remaining: &[QueryChildPattern],
579 child_index: usize,
580 anchored: bool,
581 captures: &QueryCaptures,
582) -> Vec<(usize, QueryCaptures)> {
583 match expression.quantifier {
584 QueryQuantifier::One => match_one_then_continue(
585 context,
586 expression,
587 remaining,
588 child_index,
589 anchored,
590 captures,
591 ),
592 QueryQuantifier::ZeroOrOne => {
593 let mut results =
594 match_child_patterns(context, remaining, child_index, anchored, captures.clone());
595 results.extend(match_one_then_continue(
596 context,
597 expression,
598 remaining,
599 child_index,
600 anchored,
601 captures,
602 ));
603 results
604 }
605 QueryQuantifier::ZeroOrMore => {
606 let mut results =
607 match_child_patterns(context, remaining, child_index, anchored, captures.clone());
608 results.extend(match_repeated_expression(
609 context,
610 expression,
611 remaining,
612 child_index,
613 anchored,
614 captures,
615 ));
616 results
617 }
618 QueryQuantifier::OneOrMore => match_repeated_expression(
619 context,
620 expression,
621 remaining,
622 child_index,
623 anchored,
624 captures,
625 ),
626 }
627}
628
629fn match_repeated_expression(
630 context: &MatchContext<'_>,
631 expression: &QueryChildExpression,
632 remaining: &[QueryChildPattern],
633 child_index: usize,
634 anchored: bool,
635 captures: &QueryCaptures,
636) -> Vec<(usize, QueryCaptures)> {
637 let mut results = Vec::new();
638 for (next_index, next_captures) in
639 match_expression_at_positions(context, expression, child_index, anchored, captures)
640 {
641 results.extend(match_child_patterns(
642 context,
643 remaining,
644 next_index,
645 false,
646 next_captures.clone(),
647 ));
648 results.extend(match_repeated_expression(
649 context,
650 expression,
651 remaining,
652 next_index,
653 true,
654 &next_captures,
655 ));
656 }
657 results
658}
659
660fn match_one_then_continue(
661 context: &MatchContext<'_>,
662 expression: &QueryChildExpression,
663 remaining: &[QueryChildPattern],
664 child_index: usize,
665 anchored: bool,
666 captures: &QueryCaptures,
667) -> Vec<(usize, QueryCaptures)> {
668 match_expression_at_positions(context, expression, child_index, anchored, captures)
669 .into_iter()
670 .flat_map(|(next_index, captures)| {
671 match_child_patterns(context, remaining, next_index, false, captures)
672 })
673 .collect()
674}
675
676fn match_expression_at_positions(
677 context: &MatchContext<'_>,
678 expression: &QueryChildExpression,
679 child_index: usize,
680 anchored: bool,
681 captures: &QueryCaptures,
682) -> Vec<(usize, QueryCaptures)> {
683 let positions: Box<dyn Iterator<Item = usize>> = if anchored {
684 Box::new(std::iter::once(child_index))
685 } else {
686 Box::new(child_index..context.structural_children.len())
687 };
688
689 positions
690 .filter(|position| *position < context.structural_children.len())
691 .flat_map(|position| {
692 let child = context.structural_children[position];
693 match_expression(
694 context.network,
695 context.parent,
696 child,
697 expression,
698 captures.clone(),
699 )
700 .into_iter()
701 .map(move |captures| (position + 1, captures))
702 })
703 .collect()
704}
705
706fn match_expression(
707 network: &LinkNetwork,
708 parent: LinkId,
709 child: LinkId,
710 expression: &QueryChildExpression,
711 captures: QueryCaptures,
712) -> Vec<QueryCaptures> {
713 if let Some(field) = &expression.field {
714 let field_targets = field_targets(network, parent, field);
715 if !field_targets.contains(&child) {
716 return Vec::new();
717 }
718 }
719
720 let matches = match &expression.expression {
721 QueryExpression::Node(node) => node.matches(network, child, captures),
722 QueryExpression::Alternation(alternatives) => alternatives
723 .iter()
724 .flat_map(|alternative| alternative.matches(network, child, captures.clone()))
725 .collect(),
726 };
727
728 matches
729 .into_iter()
730 .map(|captures| {
731 if let Some(capture) = &expression.capture {
732 captures.with_capture(capture, child)
733 } else {
734 captures
735 }
736 })
737 .collect()
738}
739
740fn structural_children(network: &LinkNetwork, parent: LinkId) -> Vec<LinkId> {
741 network
742 .links()
743 .filter(|link| link.references().first().copied() == Some(parent))
744 .filter(|link| {
745 !matches!(
746 link.metadata().link_type(),
747 Some(LinkType::Field | LinkType::Trivia)
748 )
749 })
750 .map(Link::id)
751 .collect()
752}
753
754fn has_field(network: &LinkNetwork, parent: LinkId, label: &str) -> bool {
755 !field_targets(network, parent, label).is_empty()
756}
757
758fn field_targets(network: &LinkNetwork, parent: LinkId, label: &str) -> Vec<LinkId> {
759 network
760 .links()
761 .filter(|link| link.metadata().link_type() == Some(LinkType::Field))
762 .filter_map(|link| {
763 let references = link.references();
764 let [field_parent, field_label, field_child] = references else {
765 return None;
766 };
767 if *field_parent != parent {
768 return None;
769 }
770 let label_link = network.link(*field_label)?;
771 (label_link.metadata().term() == Some(label)).then_some(*field_child)
772 })
773 .collect()
774}