1use rowan::SyntaxNode;
2
3use crate::Spanned;
4
5use super::parser::{Lang, SyntaxKind};
6use crate::model::*;
7
8type Node = SyntaxNode<Lang>;
9
10fn text_range(node: &Node) -> std::ops::Range<usize> {
11 let r = node.text_range();
12 r.start().into()..r.end().into()
13}
14
15fn child(node: &Node, kind: SyntaxKind) -> Option<Node> {
16 node.children().find(|c| c.kind() == kind)
17}
18
19fn children(node: &Node, kind: SyntaxKind) -> impl Iterator<Item = Node> {
20 node.children().filter(move |c| c.kind() == kind)
21}
22
23fn terminal_text(node: &Node) -> String {
24 node.text().to_string()
25}
26
27fn collect_descendants(node: &Node, kind: SyntaxKind, out: &mut Vec<Node>) {
29 for c in node.children() {
30 if c.kind() == kind {
31 out.push(c.clone());
32 }
33 collect_descendants(&c, kind, out);
34 }
35}
36
37pub fn convert(root: &Node) -> Turtle {
38 let mut base = None;
39 let mut prefixes = Vec::new();
40 let mut triples = Vec::new();
41
42 let mut prologues = Vec::new();
44 collect_descendants(root, SyntaxKind::Prologue, &mut prologues);
45 for prologue in &prologues {
46 for bd in children(prologue, SyntaxKind::BaseDecl) {
47 base = Some(Spanned(convert_base_decl(&bd), text_range(&bd)));
48 }
49 for pd in children(prologue, SyntaxKind::PrefixDecl) {
50 prefixes.push(Spanned(convert_prefix_decl(&pd), text_range(&pd)));
51 }
52 }
53
54 collect_triples(root, &mut triples);
56
57 Turtle::new(base, prefixes, triples)
58}
59
60fn collect_triples(node: &Node, triples: &mut Vec<Spanned<Triple>>) {
61 for child_node in node.children() {
62 let kind = child_node.kind();
63 match kind {
64 SyntaxKind::TriplesSameSubject => {
65 let range = text_range(&child_node);
66 triples.push(Spanned(convert_triples_same_subject(&child_node), range));
67 }
68 SyntaxKind::TriplesSameSubjectPath => {
69 let range = text_range(&child_node);
70 triples.push(Spanned(
71 convert_triples_same_subject_path(&child_node),
72 range,
73 ));
74 }
75 _ => {
76 collect_triples(&child_node, triples);
77 }
78 }
79 }
80}
81
82fn convert_base_decl(node: &Node) -> Base {
85 let range = text_range(node);
87 let nn = child(node, SyntaxKind::Iriref)
88 .map(|n| iri_from_iriref_node(&n))
89 .unwrap_or(NamedNode::Invalid);
90 let nn_range = child(node, SyntaxKind::Iriref)
91 .map(|n| text_range(&n))
92 .unwrap_or(range.clone());
93 Base(range, Spanned(nn, nn_range))
94}
95
96fn convert_prefix_decl(node: &Node) -> TurtlePrefix {
97 let range = text_range(node);
99 let prefix_text = child(node, SyntaxKind::PnameNs)
100 .map(|n| {
101 let text = terminal_text(&n);
102 let tr = text_range(&n);
103 Spanned(text.trim_end_matches(':').to_string(), tr)
104 })
105 .unwrap_or_else(|| Spanned(String::new(), range.clone()));
106
107 let value = child(node, SyntaxKind::Iriref)
108 .map(|n| {
109 let tr = text_range(&n);
110 Spanned(iri_from_iriref_node(&n), tr)
111 })
112 .unwrap_or_else(|| Spanned(NamedNode::Invalid, range.clone()));
113
114 TurtlePrefix {
115 span: range,
116 prefix: prefix_text,
117 value,
118 }
119}
120
121fn convert_triples_same_subject(node: &Node) -> Triple {
124 let (subject, po_node) = if let Some(vot) = child(node, SyntaxKind::VarOrTerm) {
126 let range = text_range(&vot);
127 (
128 Spanned(convert_var_or_term(&vot), range),
129 child(node, SyntaxKind::PropertyListNotEmpty),
130 )
131 } else if let Some(tn) = child(node, SyntaxKind::TriplesNode) {
132 let range = text_range(&tn);
133 (
134 Spanned(convert_triples_node(&tn), range),
135 child(node, SyntaxKind::PropertyList)
136 .and_then(|pl| child(&pl, SyntaxKind::PropertyListNotEmpty)),
137 )
138 } else {
139 (Spanned(Term::Invalid, text_range(node)), None)
140 };
141
142 let po = po_node
143 .map(|n| convert_property_list_not_empty(&n))
144 .unwrap_or_default();
145
146 Triple {
147 subject,
148 po,
149 graph: None,
150 }
151}
152
153fn convert_triples_same_subject_path(node: &Node) -> Triple {
156 let (subject, po_node) = if let Some(s1) = child(node, SyntaxKind::Subject1) {
160 let range = text_range(&s1);
161 let term = child(&s1, SyntaxKind::VarOrTerm)
162 .map(|v| convert_var_or_term(&v))
163 .unwrap_or(Term::Invalid);
164 (
165 Spanned(term, range),
166 child(node, SyntaxKind::PropertyListPathNotEmpty),
167 )
168 } else if let Some(s2) = child(node, SyntaxKind::Subject2) {
169 let range = text_range(&s2);
170 let term = child(&s2, SyntaxKind::TriplesNodePath)
171 .map(|t| convert_triples_node_path(&t))
172 .unwrap_or(Term::Invalid);
173 (
174 Spanned(term, range),
175 child(node, SyntaxKind::PropertyListPath)
176 .and_then(|pl| child(&pl, SyntaxKind::PropertyListPathNotEmpty)),
177 )
178 } else {
179 (Spanned(Term::Invalid, text_range(node)), None)
180 };
181
182 let po = po_node
183 .map(|n| convert_property_list_path_not_empty(&n))
184 .unwrap_or_default();
185
186 Triple {
187 subject,
188 po,
189 graph: None,
190 }
191}
192
193fn convert_property_list_not_empty(node: &Node) -> Vec<Spanned<PO>> {
196 let verbs: Vec<_> = children(node, SyntaxKind::Verb).collect();
198 let object_lists: Vec<_> = children(node, SyntaxKind::ObjectList).collect();
199
200 verbs
201 .into_iter()
202 .zip(object_lists.into_iter())
203 .map(|(v, ol)| {
204 let range = text_range(&v).start..text_range(&ol).end;
205 let pred_range = text_range(&v);
206 let predicate = convert_verb(&v);
207 let objects = convert_object_list(&ol);
208 Spanned(
209 PO {
210 predicate: Spanned(predicate, pred_range),
211 object: objects,
212 },
213 range,
214 )
215 })
216 .collect()
217}
218
219fn convert_property_list_path_not_empty(node: &Node) -> Vec<Spanned<PO>> {
220 let mut result = Vec::new();
226 let mut current_verb: Option<(Term, std::ops::Range<usize>)> = None;
227
228 for child_node in node.children() {
229 let kind = child_node.kind();
230 match kind {
231 SyntaxKind::VerbPath => {
232 current_verb = Some((convert_verb_path(&child_node), text_range(&child_node)));
233 }
234 SyntaxKind::VerbSimple => {
235 current_verb = Some((convert_verb_simple(&child_node), text_range(&child_node)));
236 }
237 SyntaxKind::ObjectListPath => {
238 if let Some((pred, pred_range)) = current_verb.take() {
239 let obj_range = text_range(&child_node);
240 let range = pred_range.start..obj_range.end;
241 let objects = convert_object_list_path(&child_node);
242 result.push(Spanned(
243 PO {
244 predicate: Spanned(pred, pred_range),
245 object: objects,
246 },
247 range,
248 ));
249 }
250 }
251 SyntaxKind::ObjectList => {
252 if let Some((pred, pred_range)) = current_verb.take() {
253 let obj_range = text_range(&child_node);
254 let range = pred_range.start..obj_range.end;
255 let objects = convert_object_list(&child_node);
256 result.push(Spanned(
257 PO {
258 predicate: Spanned(pred, pred_range),
259 object: objects,
260 },
261 range,
262 ));
263 }
264 }
265 _ => {}
266 }
267 }
268
269 result
270}
271
272fn convert_verb(node: &Node) -> Term {
275 if let Some(voi) = child(node, SyntaxKind::VarOrIri) {
277 convert_var_or_iri(&voi)
278 } else if let Some(alit) = child(node, SyntaxKind::Alit) {
279 Term::NamedNode(NamedNode::A(alit.text_range().start().into()))
280 } else {
281 Term::Invalid
282 }
283}
284
285fn convert_verb_path(node: &Node) -> Term {
286 child(node, SyntaxKind::Path)
289 .and_then(|p| child(&p, SyntaxKind::PathAlternative))
290 .and_then(|pa| child(&pa, SyntaxKind::PathSequence))
291 .and_then(|ps| child(&ps, SyntaxKind::PathEltOrInverse))
292 .and_then(|peoi| child(&peoi, SyntaxKind::PathElt))
293 .and_then(|pe| child(&pe, SyntaxKind::PathPrimary))
294 .map(|pp| convert_path_primary(&pp))
295 .unwrap_or(Term::Invalid)
296}
297
298fn convert_path_primary(node: &Node) -> Term {
299 if let Some(iri) = child(node, SyntaxKind::Iri) {
301 Term::NamedNode(convert_iri(&iri))
302 } else if let Some(alit) = child(node, SyntaxKind::Alit) {
303 Term::NamedNode(NamedNode::A(alit.text_range().start().into()))
304 } else {
305 Term::Invalid
306 }
307}
308
309fn convert_verb_simple(node: &Node) -> Term {
310 child(node, SyntaxKind::Var)
312 .map(|v| convert_var(&v))
313 .unwrap_or(Term::Invalid)
314}
315
316fn convert_object_list(node: &Node) -> Vec<Spanned<Term>> {
319 children(node, SyntaxKind::Object)
322 .map(|o| {
323 let range = text_range(&o);
324 Spanned(convert_object(&o), range)
325 })
326 .collect()
327}
328
329fn convert_object(node: &Node) -> Term {
330 child(node, SyntaxKind::GraphNode)
332 .map(|gn| convert_graph_node(&gn))
333 .unwrap_or(Term::Invalid)
334}
335
336fn convert_object_list_path(node: &Node) -> Vec<Spanned<Term>> {
337 children(node, SyntaxKind::ObjectPath)
339 .map(|op| {
340 let range = text_range(&op);
341 Spanned(convert_object_path(&op), range)
342 })
343 .collect()
344}
345
346fn convert_object_path(node: &Node) -> Term {
347 child(node, SyntaxKind::GraphNodePath)
349 .map(|gnp| convert_graph_node_path(&gnp))
350 .unwrap_or(Term::Invalid)
351}
352
353fn convert_graph_node(node: &Node) -> Term {
356 if let Some(vot) = child(node, SyntaxKind::VarOrTerm) {
358 convert_var_or_term(&vot)
359 } else if let Some(tn) = child(node, SyntaxKind::TriplesNode) {
360 convert_triples_node(&tn)
361 } else {
362 Term::Invalid
363 }
364}
365
366fn convert_graph_node_path(node: &Node) -> Term {
367 if let Some(vot) = child(node, SyntaxKind::VarOrTerm) {
369 convert_var_or_term(&vot)
370 } else if let Some(tnp) = child(node, SyntaxKind::TriplesNodePath) {
371 convert_triples_node_path(&tnp)
372 } else {
373 Term::Invalid
374 }
375}
376
377fn convert_var_or_term(node: &Node) -> Term {
380 if let Some(var) = child(node, SyntaxKind::Var) {
382 convert_var(&var)
383 } else if let Some(gt) = child(node, SyntaxKind::GraphTerm) {
384 convert_graph_term(>)
385 } else {
386 Term::Invalid
387 }
388}
389
390fn convert_var_or_iri(node: &Node) -> Term {
391 if let Some(var) = child(node, SyntaxKind::Var) {
393 convert_var(&var)
394 } else if let Some(iri) = child(node, SyntaxKind::Iri) {
395 Term::NamedNode(convert_iri(&iri))
396 } else {
397 Term::Invalid
398 }
399}
400
401fn convert_var(node: &Node) -> Term {
402 let text = child(node, SyntaxKind::Var1)
404 .or_else(|| child(node, SyntaxKind::Var2))
405 .map(|n| terminal_text(&n));
406
407 if let Some(text) = text {
408 let offset: usize = node.text_range().start().into();
409 let name = text
410 .strip_prefix('?')
411 .or_else(|| text.strip_prefix('$'))
412 .unwrap_or(&text);
413 Term::Variable(Variable(name.to_string(), offset))
414 } else {
415 Term::Invalid
416 }
417}
418
419fn convert_graph_term(node: &Node) -> Term {
420 if let Some(iri) = child(node, SyntaxKind::Iri) {
422 Term::NamedNode(convert_iri(&iri))
423 } else if let Some(rdf) = child(node, SyntaxKind::Rdfliteral) {
424 Term::Literal(Literal::RDF(convert_rdf_literal(&rdf)))
425 } else if let Some(num) = child(node, SyntaxKind::NumericLiteral) {
426 Term::Literal(convert_numeric_literal(&num))
427 } else if let Some(b) = child(node, SyntaxKind::BooleanLiteral) {
428 Term::Literal(Literal::Boolean(b.text().to_string().trim() == "true"))
429 } else if let Some(bn) = child(node, SyntaxKind::BlankNode) {
430 Term::BlankNode(convert_blank_node(&bn))
431 } else if child(node, SyntaxKind::Nil).is_some() {
432 Term::Collection(Vec::new())
433 } else {
434 Term::Invalid
435 }
436}
437
438fn convert_triples_node(node: &Node) -> Term {
441 if let Some(coll) = child(node, SyntaxKind::Collection) {
443 Term::Collection(convert_collection(&coll))
444 } else if let Some(bpl) = child(node, SyntaxKind::BlankNodePropertyList) {
445 convert_blank_node_property_list(&bpl)
446 } else {
447 Term::Invalid
448 }
449}
450
451fn convert_triples_node_path(node: &Node) -> Term {
452 if let Some(coll) = child(node, SyntaxKind::CollectionPath) {
454 Term::Collection(convert_collection_path(&coll))
455 } else if let Some(bplp) = child(node, SyntaxKind::BlankNodePropertyListPath) {
456 convert_blank_node_property_list_path(&bplp)
457 } else {
458 Term::Invalid
459 }
460}
461
462fn convert_collection(node: &Node) -> Vec<Spanned<Term>> {
463 children(node, SyntaxKind::GraphNode)
465 .map(|gn| {
466 let range = text_range(&gn);
467 Spanned(convert_graph_node(&gn), range)
468 })
469 .collect()
470}
471
472fn convert_collection_path(node: &Node) -> Vec<Spanned<Term>> {
473 children(node, SyntaxKind::GraphNodePath)
475 .map(|gnp| {
476 let range = text_range(&gnp);
477 Spanned(convert_graph_node_path(&gnp), range)
478 })
479 .collect()
480}
481
482fn convert_blank_node_property_list(node: &Node) -> Term {
483 let offset: usize = node.text_range().start().into();
485 let end: usize = node.text_range().end().into();
486 let po = child(node, SyntaxKind::PropertyListNotEmpty)
487 .map(|n| convert_property_list_not_empty(&n))
488 .unwrap_or_default();
489 Term::BlankNode(BlankNode::Unnamed(po, offset, end))
490}
491
492fn convert_blank_node_property_list_path(node: &Node) -> Term {
493 let offset: usize = node.text_range().start().into();
495 let end: usize = node.text_range().end().into();
496 let po = child(node, SyntaxKind::PropertyListPathNotEmpty)
497 .map(|n| convert_property_list_path_not_empty(&n))
498 .unwrap_or_default();
499 Term::BlankNode(BlankNode::Unnamed(po, offset, end))
500}
501
502fn convert_iri(node: &Node) -> NamedNode {
505 if let Some(iriref_node) = child(node, SyntaxKind::Iriref) {
506 iri_from_iriref_node(&iriref_node)
507 } else if let Some(pn) = child(node, SyntaxKind::PrefixedName) {
508 convert_prefixed_name(&pn)
509 } else {
510 NamedNode::Invalid
511 }
512}
513
514fn iri_from_iriref_node(node: &Node) -> NamedNode {
515 let text = terminal_text(node);
516 let offset: usize = node.text_range().start().into();
517 let inner = text.trim_start_matches('<').trim_end_matches('>');
518 NamedNode::Full(inner.to_string(), offset)
519}
520
521fn convert_prefixed_name(node: &Node) -> NamedNode {
522 if let Some(pname_ln) = child(node, SyntaxKind::PnameLn) {
523 let text = terminal_text(&pname_ln);
524 let offset: usize = pname_ln.text_range().start().into();
525 let (prefix, value) = text.split_once(':').unwrap_or((&text, ""));
526 NamedNode::Prefixed {
527 prefix: prefix.to_string(),
528 value: value.to_string(),
529 idx: offset,
530 computed: None,
531 }
532 } else if let Some(pname_ns) = child(node, SyntaxKind::PnameNs) {
533 let text = terminal_text(&pname_ns);
534 let offset: usize = pname_ns.text_range().start().into();
535 let prefix = text.trim_end_matches(':');
536 NamedNode::Prefixed {
537 prefix: prefix.to_string(),
538 value: String::new(),
539 idx: offset,
540 computed: None,
541 }
542 } else {
543 NamedNode::Invalid
544 }
545}
546
547fn convert_blank_node(node: &Node) -> BlankNode {
548 if let Some(label) = child(node, SyntaxKind::BlankNodeLabel) {
549 let text = terminal_text(&label);
550 let offset: usize = label.text_range().start().into();
551 let name = text.strip_prefix("_:").unwrap_or(&text);
552 BlankNode::Named(name.to_string(), offset)
553 } else {
554 let offset: usize = node.text_range().start().into();
555 BlankNode::Unnamed(Vec::new(), offset, offset)
556 }
557}
558
559fn convert_numeric_literal(node: &Node) -> Literal {
560 Literal::Numeric(node.text().to_string().trim().to_string())
562}
563
564fn convert_rdf_literal(node: &Node) -> RDFLiteral {
565 let offset: usize = node.text_range().start().into();
567
568 let (value, quote_style, len) = if let Some(str_node) = child(node, SyntaxKind::MyString) {
569 let string_node = str_node.children().next();
570 if let Some(sn) = string_node {
571 let text = terminal_text(&sn);
572 let (inner, style) = strip_string_delimiters(&text);
573 (inner.to_string(), style, 1)
574 } else {
575 (String::new(), StringStyle::Double, 0)
576 }
577 } else {
578 (String::new(), StringStyle::Double, 0)
579 };
580
581 let lang = child(node, SyntaxKind::Langtag).map(|n| {
582 let span = text_range(&n);
583 let text = terminal_text(&n);
584 let value = text.strip_prefix('@').unwrap_or(&text).to_string();
585 Spanned(value, span)
586 });
587
588 let ty = child(node, SyntaxKind::Iri).map(|n| Spanned(convert_iri(&n), text_range(&n)));
589
590 RDFLiteral {
591 value,
592 quote_style,
593 lang,
594 ty,
595 idx: offset,
596 len,
597 }
598}
599
600fn strip_string_delimiters(text: &str) -> (&str, StringStyle) {
601 if let Some(inner) = text
602 .strip_prefix("\"\"\"")
603 .and_then(|s| s.strip_suffix("\"\"\""))
604 {
605 (inner, StringStyle::DoubleLong)
606 } else if let Some(inner) = text.strip_prefix("'''").and_then(|s| s.strip_suffix("'''")) {
607 (inner, StringStyle::SingleLong)
608 } else if let Some(inner) = text.strip_prefix('"').and_then(|s| s.strip_suffix('"')) {
609 (inner, StringStyle::Double)
610 } else if let Some(inner) = text.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) {
611 (inner, StringStyle::Single)
612 } else {
613 (text, StringStyle::Double)
614 }
615}
616
617#[cfg(test)]
618mod tests {
619 use super::*;
620 use crate::{parse as crate_parse, sparql::parser as lang};
621
622 fn parse(input: &str) -> Turtle {
623 let (result, _) = crate_parse(lang::Rule::new(lang::SyntaxKind::QueryUnit), input);
624 let root = result.syntax::<lang::Lang>();
625 convert(&root)
626 }
627
628 fn parse_update(input: &str) -> Turtle {
629 let (result, _) = crate_parse(lang::Rule::new(lang::SyntaxKind::UpdateUnit), input);
630 let root = result.syntax::<lang::Lang>();
631 convert(&root)
632 }
633
634 fn prefixed(prefix: &str, value: &str) -> NamedNode {
637 NamedNode::Prefixed {
638 prefix: prefix.to_string(),
639 value: value.to_string(),
640 idx: 0,
641 computed: None,
642 }
643 }
644
645 fn nn_eq(a: &NamedNode, b: &NamedNode) -> bool {
646 match (a, b) {
647 (NamedNode::Full(s1, _), NamedNode::Full(s2, _)) => s1 == s2,
648 (
649 NamedNode::Prefixed {
650 prefix: p1,
651 value: v1,
652 ..
653 },
654 NamedNode::Prefixed {
655 prefix: p2,
656 value: v2,
657 ..
658 },
659 ) => p1 == p2 && v1 == v2,
660 (NamedNode::A(_), NamedNode::A(_)) => true,
661 (NamedNode::Invalid, NamedNode::Invalid) => true,
662 _ => false,
663 }
664 }
665
666 fn term_nn(t: &Term) -> &NamedNode {
667 match t {
668 Term::NamedNode(nn) => nn,
669 other => panic!("expected NamedNode, got {:?}", other),
670 }
671 }
672
673 fn term_var(t: &Term) -> &str {
674 match t {
675 Term::Variable(Variable(name, _)) => name.as_str(),
676 other => panic!("expected Variable, got {:?}", other),
677 }
678 }
679
680 fn term_lit(t: &Term) -> &Literal {
681 match t {
682 Term::Literal(l) => l,
683 other => panic!("expected Literal, got {:?}", other),
684 }
685 }
686
687 fn term_bn(t: &Term) -> &BlankNode {
688 match t {
689 Term::BlankNode(bn) => bn,
690 other => panic!("expected BlankNode, got {:?}", other),
691 }
692 }
693
694 #[test]
697 fn test_prefix_decl() {
698 let doc = parse("PREFIX ex: <http://example.org/> SELECT * WHERE { ?s ?p ?o }");
699 assert_eq!(doc.prefixes.len(), 1);
700 assert_eq!(doc.prefixes[0].prefix.value(), "ex");
701 assert!(nn_eq(
702 doc.prefixes[0].value.value(),
703 &NamedNode::Full("http://example.org/".to_string(), 0)
704 ));
705 }
706
707 #[test]
708 fn test_base_decl() {
709 let doc = parse("BASE <http://example.org/> SELECT * WHERE { ?s ?p ?o }");
710 let base = doc.base.as_ref().expect("base should be present");
711 assert!(nn_eq(
712 base.value().1.value(),
713 &NamedNode::Full("http://example.org/".to_string(), 0)
714 ));
715 }
716
717 #[test]
718 fn test_multiple_prefixes() {
719 let doc = parse(
720 "PREFIX ex: <http://example.org/> PREFIX foaf: <http://xmlns.com/foaf/0.1/> SELECT * WHERE { ?s ?p ?o }",
721 );
722 assert_eq!(doc.prefixes.len(), 2);
723 assert_eq!(doc.prefixes[0].prefix.value(), "ex");
724 assert_eq!(doc.prefixes[1].prefix.value(), "foaf");
725 }
726
727 #[test]
730 fn test_select_basic_triple_pattern() {
731 let doc = parse("SELECT * WHERE { ?s ?p ?o }");
732 assert_eq!(doc.triples.len(), 1);
733 let t = doc.triples[0].value();
734 assert_eq!(term_var(t.subject.value()), "s");
735 assert_eq!(term_var(t.po[0].predicate.value()), "p");
736 assert_eq!(term_var(t.po[0].object[0].value()), "o");
737 }
738
739 #[test]
742 fn test_triple_with_iris() {
743 let doc =
744 parse("PREFIX ex: <http://example.org/> SELECT * WHERE { ex:alice ex:knows ex:bob }");
745 assert_eq!(doc.triples.len(), 1);
746 let t = doc.triples[0].value();
747 assert!(nn_eq(term_nn(t.subject.value()), &prefixed("ex", "alice")));
748 assert!(nn_eq(
749 term_nn(t.po[0].predicate.value()),
750 &prefixed("ex", "knows")
751 ));
752 assert!(nn_eq(
753 term_nn(t.po[0].object[0].value()),
754 &prefixed("ex", "bob")
755 ));
756 }
757
758 #[test]
761 fn test_a_shorthand() {
762 let doc =
763 parse("PREFIX foaf: <http://xmlns.com/foaf/0.1/> SELECT * WHERE { ?x a foaf:Person }");
764 let t = doc.triples[0].value();
765 assert!(matches!(
766 t.po[0].predicate.value(),
767 Term::NamedNode(NamedNode::A(_))
768 ));
769 }
770
771 #[test]
774 fn test_multiple_triple_patterns() {
775 let doc = parse("SELECT * WHERE { ?s ?p ?o . ?a ?b ?c }");
776 assert_eq!(doc.triples.len(), 2);
777 assert_eq!(term_var(doc.triples[0].subject.value()), "s");
778 assert_eq!(term_var(doc.triples[1].subject.value()), "a");
779 }
780
781 #[test]
784 fn test_semicolon_multiple_po() {
785 let doc = parse(
786 "PREFIX ex: <http://example.org/> SELECT * WHERE { ?x ex:name ?name ; ex:age ?age }",
787 );
788 let t = doc.triples[0].value();
789 assert_eq!(t.po.len(), 2);
790 assert!(nn_eq(
791 term_nn(t.po[0].predicate.value()),
792 &prefixed("ex", "name")
793 ));
794 assert!(nn_eq(
795 term_nn(t.po[1].predicate.value()),
796 &prefixed("ex", "age")
797 ));
798 }
799
800 #[test]
803 fn test_comma_multiple_objects() {
804 let doc = parse(
805 "PREFIX ex: <http://example.org/> SELECT * WHERE { ?x ex:knows ex:alice , ex:bob }",
806 );
807 let t = doc.triples[0].value();
808 assert_eq!(t.po[0].object.len(), 2);
809 }
810
811 #[test]
814 fn test_string_literal() {
815 let doc = parse("PREFIX ex: <http://example.org/> SELECT * WHERE { ?x ex:name \"Alice\" }");
816 let t = doc.triples[0].value();
817 assert_eq!(term_lit(t.po[0].object[0].value()).plain_string(), "Alice");
818 }
819
820 #[test]
821 fn test_numeric_literal() {
822 let doc = parse("PREFIX ex: <http://example.org/> SELECT * WHERE { ?x ex:age 30 }");
823 let t = doc.triples[0].value();
824 match term_lit(t.po[0].object[0].value()) {
825 Literal::Numeric(n) => assert_eq!(n, "30"),
826 other => panic!("expected Numeric, got {:?}", other),
827 }
828 }
829
830 #[test]
831 fn test_boolean_literal() {
832 let doc = parse("PREFIX ex: <http://example.org/> SELECT * WHERE { ?x ex:active true }");
833 let t = doc.triples[0].value();
834 assert_eq!(*term_lit(t.po[0].object[0].value()), Literal::Boolean(true));
835 }
836
837 #[test]
840 fn test_blank_node_property_list() {
841 let doc = parse(
842 "PREFIX ex: <http://example.org/> SELECT * WHERE { ?x ex:knows [ ex:name \"Bob\" ] }",
843 );
844 let t = doc.triples[0].value();
845 match term_bn(t.po[0].object[0].value()) {
846 BlankNode::Unnamed(pos, _, _) => {
847 assert_eq!(pos.len(), 1);
848 assert!(nn_eq(
849 term_nn(pos[0].predicate.value()),
850 &prefixed("ex", "name")
851 ));
852 }
853 other => panic!("expected Unnamed blank node, got {:?}", other),
854 }
855 }
856
857 #[test]
860 fn test_construct_template_triples() {
861 let doc = parse(
862 "PREFIX ex: <http://example.org/> CONSTRUCT { ?s ex:knows ?o } WHERE { ?s ex:knows ?o }",
863 );
864 assert!(doc.triples.len() >= 2);
866 }
867
868 #[test]
871 fn test_optional_triples_collected() {
872 let doc = parse(
873 "PREFIX ex: <http://example.org/> SELECT * WHERE { ?x a ex:Person . OPTIONAL { ?x ex:name ?name } }",
874 );
875 assert_eq!(doc.triples.len(), 2);
877 }
878
879 #[test]
880 fn test_union_triples_collected() {
881 let doc = parse(
882 "PREFIX ex: <http://example.org/> SELECT * WHERE { { ?x ex:name ?n } UNION { ?x ex:label ?n } }",
883 );
884 assert_eq!(doc.triples.len(), 2);
885 }
886
887 #[test]
890 fn test_ask_query() {
891 let doc = parse("PREFIX ex: <http://example.org/> ASK { ex:alice ex:knows ex:bob }");
892 assert_eq!(doc.triples.len(), 1);
893 }
894
895 #[test]
898 fn test_mixed_variables_and_iris() {
899 let doc =
900 parse("PREFIX ex: <http://example.org/> SELECT ?name WHERE { ex:alice ex:name ?name }");
901 assert_eq!(doc.triples.len(), 1);
902 let t = doc.triples[0].value();
903 assert!(nn_eq(term_nn(t.subject.value()), &prefixed("ex", "alice")));
904 assert_eq!(term_var(t.po[0].object[0].value()), "name");
905 }
906
907 #[test]
910 fn test_insert_data() {
911 let doc = parse_update(
912 "PREFIX ex: <http://example.org/> INSERT DATA { ex:alice ex:knows ex:bob }",
913 );
914 assert_eq!(doc.triples.len(), 1);
915 let t = doc.triples[0].value();
916 assert!(nn_eq(term_nn(t.subject.value()), &prefixed("ex", "alice")));
917 }
918
919 use crate::{IncrementalBias, PrevParseInfo, TokenTrait, parse_incremental};
922
923 fn prev_info(text: &str) -> PrevParseInfo {
924 let (_, tokens) = crate_parse(lang::Rule::new(lang::SyntaxKind::QueryUnit), text);
925 let mut depth: i32 = 0;
926 PrevParseInfo {
927 tokens: tokens
928 .iter()
929 .map(|t| {
930 let d = depth.clamp(0, 255) as u8;
931 depth += t.kind.bracket_delta() as i32;
932 t.to_prev_token(d)
933 })
934 .collect(),
935 had_errors: false,
936 }
937 }
938
939 #[test]
963 fn test_suggest_filter_moved_into_optional() {
964 let before =
965 "SELECT * WHERE {\n OPTIONAL { ?person foaf:age ?age . }\n FILTER(?name != \"\")\n}";
966 let after = "SELECT * WHERE {\n OPTIONAL { ?person foaf:age ?age .\n FILTER(?name != \"\")\n }\n}";
967
968 let prev = prev_info(before);
969 let bias = IncrementalBias::default();
970 let (parse, _) = parse_incremental(
971 lang::Rule::new(lang::SyntaxKind::QueryUnit),
972 after,
973 Some(&prev),
974 bias,
975 );
976
977 let errors: Vec<_> = parse.errors.iter().collect();
978 assert_eq!(
979 errors.len(),
980 0,
981 "valid SPARQL should parse without errors; got: {:?}",
982 errors
983 );
984
985 let root = parse.syntax::<lang::Lang>();
986 let doc = convert(&root);
987 assert_eq!(
988 doc.triples.len(),
989 1,
990 "should produce one triple (?person foaf:age ?age)"
991 );
992 }
993}
994
995#[cfg(test)]
996mod demo_sim_tests {
997 use crate::sparql::parser as lang;
998 use crate::{IncrementalBias, parse_incremental};
999
1000 #[test]
1002 fn test_suggest_filter_moved_demo_flow() {
1003 let before =
1004 "SELECT * WHERE {\n OPTIONAL { ?person foaf:age ?age . }\n FILTER(?name != \"\")\n}";
1005 let after = "SELECT * WHERE {\n OPTIONAL { ?person foaf:age ?age .\n FILTER(?name != \"\")\n }\n}";
1006
1007 let (_, prev) = parse_incremental(
1009 lang::Rule::new(lang::SyntaxKind::QueryUnit),
1010 before,
1011 None,
1012 IncrementalBias::default(),
1013 );
1014
1015 let (parse, _) = parse_incremental(
1017 lang::Rule::new(lang::SyntaxKind::QueryUnit),
1018 after,
1019 Some(&prev),
1020 IncrementalBias::default(),
1021 );
1022
1023 let errors: Vec<_> = parse.errors.iter().collect();
1024 assert_eq!(
1025 errors.len(),
1026 0,
1027 "demo flow: valid SPARQL should parse without errors; got: {:?}",
1028 errors
1029 );
1030 }
1031}
1032
1033#[cfg(test)]
1034mod real_demo_tests {
1035 use crate::sparql::parser as lang;
1036 use crate::{IncrementalBias, parse_incremental};
1037
1038 #[test]
1039 fn test_suggest_filter_moved_real_demo() {
1040 let before = r#"
1041PREFIX ex: <http://example.org/>
1042PREFIX foaf: <http://xmlns.com/foaf/0.1/>
1043
1044SELECT ?person ?name ?age
1045WHERE {
1046 ?person a foaf:Person ;
1047 foaf:name ?name .
1048 OPTIONAL { ?person foaf:age ?age . }
1049 FILTER(?name != "")
1050}
1051ORDER BY ?name"#;
1052
1053 let after_1 = r#"
1054PREFIX ex: <http://example.org/>
1055PREFIX foaf: <http://xmlns.com/foaf/0.1/>
1056
1057SELECT ?person ?name ?age
1058WHERE {
1059 ?person a foaf:Person ;
1060 foaf:name ?name .
1061 OPTIONAL { ?person foaf:age ?age .
1062 FILTER(?name != "")
1063}
1064ORDER BY ?name"#;
1065
1066 let after_2 = r#"
1067PREFIX ex: <http://example.org/>
1068PREFIX foaf: <http://xmlns.com/foaf/0.1/>
1069
1070SELECT ?person ?name ?age
1071WHERE {
1072 ?person a foaf:Person ;
1073 foaf:name ?name .
1074 OPTIONAL { ?person foaf:age ?age .
1075 FILTER(?name != "")}
1076}
1077ORDER BY ?name"#;
1078
1079 let (_, prev) = parse_incremental(
1081 lang::Rule::new(lang::SyntaxKind::QueryUnit),
1082 before,
1083 None,
1084 IncrementalBias::default(),
1085 );
1086
1087 let (_, prev) = parse_incremental(
1088 lang::Rule::new(lang::SyntaxKind::QueryUnit),
1089 after_1,
1090 Some(&prev),
1091 IncrementalBias::default(),
1092 );
1093 let (parse, _) = parse_incremental(
1094 lang::Rule::new(lang::SyntaxKind::QueryUnit),
1095 after_2,
1096 Some(&prev),
1097 IncrementalBias::default(),
1098 );
1099
1100 let errors: Vec<_> = parse.errors.iter().collect();
1101 assert_eq!(
1102 errors.len(),
1103 0,
1104 "real demo: valid SPARQL should parse without errors; got: {:?}",
1105 errors
1106 );
1107 }
1108}
1109
1110#[cfg(test)]
1111mod multi_step_tests {
1112 use crate::sparql::parser as lang;
1113 use crate::{IncrementalBias, parse_incremental};
1114
1115 #[test]
1116 fn test_filter_move_via_intermediate_delete() {
1117 let step1 = "\
1119PREFIX ex: <http://example.org/>
1120PREFIX foaf: <http://xmlns.com/foaf/0.1/>
1121
1122SELECT ?person ?name ?age
1123WHERE {
1124 ?person a foaf:Person ;
1125 foaf:name ?name .
1126 OPTIONAL { ?person foaf:age ?age . }
1127 FILTER(?name != \"\")
1128}
1129ORDER BY ?name";
1130
1131 let step2 = "\
1133PREFIX ex: <http://example.org/>
1134PREFIX foaf: <http://xmlns.com/foaf/0.1/>
1135
1136SELECT ?person ?name ?age
1137WHERE {
1138 ?person a foaf:Person ;
1139 foaf:name ?name .
1140 OPTIONAL { ?person foaf:age ?age .
1141 FILTER(?name != \"\")
1142}
1143ORDER BY ?name";
1144
1145 let step3 = "\
1147PREFIX ex: <http://example.org/>
1148PREFIX foaf: <http://xmlns.com/foaf/0.1/>
1149
1150SELECT ?person ?name ?age
1151WHERE {
1152 ?person a foaf:Person ;
1153 foaf:name ?name .
1154 OPTIONAL { ?person foaf:age ?age .
1155 FILTER(?name != \"\")}
1156}
1157ORDER BY ?name";
1158
1159 let bias = IncrementalBias::default();
1160 let rule = || lang::Rule::new(lang::SyntaxKind::QueryUnit);
1161
1162 let (parse1, prev1) = parse_incremental(rule(), step1, None, bias);
1163 eprintln!("Step 1 errors: {:?}", parse1.errors);
1164
1165 let (parse2, prev2) = parse_incremental(rule(), step2, Some(&prev1), bias);
1166 eprintln!("Step 2 errors: {:?}", parse2.errors);
1167
1168 for tok in &prev2.tokens {
1170 if tok.text.contains("FILTER") || tok.text == "(" {
1171 eprintln!(
1172 " token {:?} fp={:?} depth={}",
1173 tok.text,
1174 tok.fingerprint.is_some(),
1175 tok.depth
1176 );
1177 }
1178 }
1179
1180 let (parse3, _) = parse_incremental(rule(), step3, Some(&prev2), bias);
1181 let errors: Vec<_> = parse3.errors.iter().collect();
1182 eprintln!("Step 3 errors: {:?}", errors);
1183 assert_eq!(
1184 errors.len(),
1185 0,
1186 "three-step edit: valid SPARQL should parse without errors; got: {:?}",
1187 errors
1188 );
1189 }
1190}