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 {
25 node.text().to_string()
26}
27
28pub fn convert(root: &Node) -> Turtle {
29 let mut base = None;
32 let mut prefixes = Vec::new();
33 let mut triples = Vec::new();
34
35 for child_node in root.children() {
36 let kind = child_node.kind();
37 if kind == SyntaxKind::Directive {
38 if let Some(b) = child(&child_node, SyntaxKind::Base)
39 .or_else(|| child(&child_node, SyntaxKind::SparqlBase))
40 {
41 base = Some(Spanned(convert_base(&b), text_range(&b)));
42 } else if let Some(p) = child(&child_node, SyntaxKind::PrefixId)
43 .or_else(|| child(&child_node, SyntaxKind::SparqlPrefix))
44 {
45 prefixes.push(Spanned(convert_prefix(&p), text_range(&p)));
46 }
47 } else if kind == SyntaxKind::Block {
48 convert_block(&child_node, &mut triples);
49 }
50 }
51
52 Turtle::new(base, prefixes, triples)
53}
54
55fn convert_block(block: &Node, triples: &mut Vec<Spanned<Triple>>) {
58 if let Some(gb) = child(block, SyntaxKind::GraphBlock) {
61 convert_graph_block(&gb, triples);
62 } else if let Some(t2) = child(block, SyntaxKind::Triples) {
63 let range = text_range(block);
64 triples.push(Spanned(convert_triples(&t2), range));
65 }
66}
67
68fn convert_graph_block(node: &Node, triples: &mut Vec<Spanned<Triple>>) {
69 let graph_term = child(node, SyntaxKind::GraphTerm).map(|los| {
72 let range = text_range(&los);
73 Spanned(convert_label_or_subject(&los), range)
74 });
75 if let Some(wg) = child(node, SyntaxKind::WrappedGraph) {
76 convert_wrapped_graph(&wg, graph_term, triples);
77 }
78}
79
80fn convert_wrapped_graph(
81 node: &Node,
82 graph: Option<Spanned<Term>>,
83 triples: &mut Vec<Spanned<Triple>>,
84) {
85 if let Some(tb) = child(node, SyntaxKind::TriplesBlock) {
87 convert_triples_block(&tb, &graph, triples);
88 }
89}
90
91fn convert_triples_block(
92 node: &Node,
93 graph: &Option<Spanned<Term>>,
94 triples: &mut Vec<Spanned<Triple>>,
95) {
96 if let Some(t) = child(node, SyntaxKind::Triples) {
98 let range = text_range(&t);
99 let mut triple = convert_triples(&t);
100 triple.graph = graph.clone();
101 triples.push(Spanned(triple, range));
102 }
103 if let Some(tb) = child(node, SyntaxKind::TriplesBlock) {
105 convert_triples_block(&tb, graph, triples);
106 }
107}
108
109fn convert_label_or_subject(node: &Node) -> Term {
110 if let Some(iri) = child(node, SyntaxKind::Iri) {
112 Term::NamedNode(convert_iri(&iri))
113 } else if let Some(bn) = child(node, SyntaxKind::BlankNode) {
114 Term::BlankNode(convert_blank_node(&bn))
115 } else {
116 Term::Invalid
117 }
118}
119
120fn convert_base(node: &Node) -> Base {
123 let range = text_range(node);
124 let nn = child(node, SyntaxKind::Iriref)
125 .map(|n| iri_from_iriref_node(&n))
126 .or_else(|| child(node, SyntaxKind::Iri).map(|n| convert_iri(&n)))
127 .unwrap_or(NamedNode::Invalid);
128
129 let nn_range = child(node, SyntaxKind::Iriref)
130 .or_else(|| child(node, SyntaxKind::Iri))
131 .map(|n| text_range(&n))
132 .unwrap_or(range.clone());
133
134 Base(range, Spanned(nn, nn_range))
135}
136
137fn convert_prefix(node: &Node) -> TurtlePrefix {
138 let range = text_range(node);
139
140 let prefix_text = child(node, SyntaxKind::PnameNs)
141 .map(|n| {
142 let text = terminal_text(&n);
143 let tr = text_range(&n);
144 Spanned(text.trim_end_matches(':').to_string(), tr)
145 })
146 .unwrap_or_else(|| Spanned(String::new(), range.clone()));
147
148 let value = child(node, SyntaxKind::Iriref)
149 .map(|n| {
150 let tr = text_range(&n);
151 Spanned(iri_from_iriref_node(&n), tr)
152 })
153 .or_else(|| {
154 child(node, SyntaxKind::Iri).map(|n| {
155 let r = text_range(&n);
156 Spanned(convert_iri(&n), r)
157 })
158 })
159 .unwrap_or_else(|| Spanned(NamedNode::Invalid, range.clone()));
160
161 TurtlePrefix {
162 span: range,
163 prefix: prefix_text,
164 value,
165 }
166}
167
168fn convert_triples(node: &Node) -> Triple {
169 let (subject, po_node) = if let Some(bpl) = child(node, SyntaxKind::BlankNodePropertyList) {
171 let range = text_range(&bpl);
172 let subject = convert_blank_node_property_list(&bpl);
173 (
174 Spanned(subject, range),
175 child(node, SyntaxKind::PredicateObjectList),
176 )
177 } else if let Some(subj) = child(node, SyntaxKind::Subject) {
178 let range = text_range(&subj);
179 (
180 Spanned(convert_subject(&subj), range),
181 child(node, SyntaxKind::PredicateObjectList),
182 )
183 } else {
184 (Spanned(Term::Invalid, text_range(node)), None)
185 };
186
187 let po = po_node
188 .map(|n| convert_predicate_object_list(&n))
189 .unwrap_or_default();
190
191 Triple {
192 subject,
193 po,
194 graph: None,
195 }
196}
197
198fn convert_predicate_object_list(node: &Node) -> Vec<Spanned<PO>> {
199 let verbs: Vec<_> = children(node, SyntaxKind::Verb).collect();
200 let object_lists: Vec<_> = children(node, SyntaxKind::ObjectList).collect();
201
202 verbs
203 .into_iter()
204 .zip(object_lists.into_iter())
205 .map(|(v, ol)| {
206 let range = text_range(&v).start..text_range(&ol).end;
207 let pred_range = text_range(&v);
208 let predicate = convert_verb(&v);
209 let objects = convert_object_list(&ol);
210 Spanned(
211 PO {
212 predicate: Spanned(predicate, pred_range),
213 object: objects,
214 },
215 range,
216 )
217 })
218 .collect()
219}
220
221fn convert_verb(node: &Node) -> Term {
222 if let Some(pred) = child(node, SyntaxKind::Predicate) {
223 if let Some(iri) = child(&pred, SyntaxKind::Iri) {
224 Term::NamedNode(convert_iri(&iri))
225 } else {
226 Term::Invalid
227 }
228 } else if let Some(alit) = child(node, SyntaxKind::Alit) {
229 Term::NamedNode(NamedNode::A(alit.text_range().start().into()))
230 } else {
231 Term::Invalid
232 }
233}
234
235fn convert_object_list(node: &Node) -> Vec<Spanned<Term>> {
236 children(node, SyntaxKind::Object)
237 .map(|o| {
238 let range = text_range(&o);
239 Spanned(convert_object(&o), range)
240 })
241 .collect()
242}
243
244fn convert_subject(node: &Node) -> Term {
245 if let Some(iri) = child(node, SyntaxKind::Iri) {
248 Term::NamedNode(convert_iri(&iri))
249 } else {
250 convert_blank(node)
251 }
252}
253
254fn convert_blank(node: &Node) -> Term {
255 if let Some(bn) = child(node, SyntaxKind::BlankNode) {
257 Term::BlankNode(convert_blank_node(&bn))
258 } else if let Some(coll) = child(node, SyntaxKind::Collection) {
259 Term::Collection(convert_collection(&coll))
260 } else {
261 Term::Invalid
262 }
263}
264
265fn convert_object(node: &Node) -> Term {
266 if let Some(iri) = child(node, SyntaxKind::Iri) {
268 Term::NamedNode(convert_iri(&iri))
269 } else if let Some(bpl) = child(node, SyntaxKind::BlankNodePropertyList2) {
270 convert_blank_node_property_list(&bpl)
271 } else if let Some(bpl) = child(node, SyntaxKind::BlankNodePropertyList) {
272 convert_blank_node_property_list(&bpl)
273 } else if let Some(lit) = child(node, SyntaxKind::Literal) {
274 Term::Literal(convert_literal(&lit))
275 } else {
276 convert_blank(&node)
277 }
278}
279
280fn convert_iri(node: &Node) -> NamedNode {
281 if let Some(iriref_node) = child(node, SyntaxKind::Iriref) {
282 iri_from_iriref_node(&iriref_node)
283 } else if let Some(pn) = child(node, SyntaxKind::PrefixedName) {
284 convert_prefixed_name(&pn)
285 } else {
286 NamedNode::Invalid
287 }
288}
289
290fn iri_from_iriref_node(node: &Node) -> NamedNode {
291 let text = terminal_text(node);
292 let offset: usize = node.text_range().start().into();
293 let inner = text.trim_start_matches('<').trim_end_matches('>');
294 NamedNode::Full(inner.to_string(), offset)
295}
296
297fn convert_prefixed_name(node: &Node) -> NamedNode {
298 if let Some(pname_ln) = child(node, SyntaxKind::PnameLn) {
299 let text = terminal_text(&pname_ln);
300 let offset: usize = pname_ln.text_range().start().into();
301 let (prefix, value) = text.split_once(':').unwrap_or((&text, ""));
302 NamedNode::Prefixed {
303 prefix: prefix.to_string(),
304 value: value.to_string(),
305 idx: offset,
306 computed: None,
307 }
308 } else if let Some(pname_ns) = child(node, SyntaxKind::PnameNs) {
309 let text = terminal_text(&pname_ns);
310 let offset: usize = pname_ns.text_range().start().into();
311 let prefix = text.trim_end_matches(':');
312 NamedNode::Prefixed {
313 prefix: prefix.to_string(),
314 value: String::new(),
315 idx: offset,
316 computed: None,
317 }
318 } else {
319 NamedNode::Invalid
320 }
321}
322
323fn convert_blank_node(node: &Node) -> BlankNode {
324 if let Some(label) = child(node, SyntaxKind::BlankNodeLabel) {
325 let text = terminal_text(&label);
326 let offset: usize = label.text_range().start().into();
327 let name = text.strip_prefix("_:").unwrap_or(&text);
328 BlankNode::Named(name.to_string(), offset)
329 } else {
330 let offset: usize = node.text_range().start().into();
331 BlankNode::Unnamed(Vec::new(), offset, offset)
332 }
333}
334
335fn convert_blank_node_property_list(node: &Node) -> Term {
336 let offset: usize = node.text_range().start().into();
337 let end: usize = node.text_range().end().into();
338 let po = child(node, SyntaxKind::PredicateObjectList)
339 .map(|n| convert_predicate_object_list(&n))
340 .unwrap_or_default();
341 Term::BlankNode(BlankNode::Unnamed(po, offset, end))
342}
343
344fn convert_collection(node: &Node) -> Vec<Spanned<Term>> {
345 children(node, SyntaxKind::Object)
346 .map(|o| {
347 let range = text_range(&o);
348 Spanned(convert_object(&o), range)
349 })
350 .collect()
351}
352
353fn convert_literal(node: &Node) -> Literal {
354 if let Some(rdf) = child(node, SyntaxKind::Rdfliteral) {
355 Literal::RDF(convert_rdf_literal(&rdf))
356 } else if let Some(num) = child(node, SyntaxKind::NumericLiteral) {
357 Literal::Numeric(num.text().to_string().trim().to_string())
358 } else if let Some(b) = child(node, SyntaxKind::BooleanLiteral) {
359 Literal::Boolean(b.text().to_string().trim() == "true")
360 } else {
361 Literal::Numeric(String::new())
362 }
363}
364
365fn convert_rdf_literal(node: &Node) -> RDFLiteral {
366 let offset: usize = node.text_range().start().into();
367
368 let (value, quote_style, len) = if let Some(str_node) = child(node, SyntaxKind::MyString) {
369 let string_node = str_node.children().next();
370 if let Some(sn) = string_node {
371 let text = terminal_text(&sn);
372 let (inner, style) = strip_string_delimiters(&text);
373 (inner.to_string(), style, 1)
374 } else {
375 (String::new(), StringStyle::Double, 0)
376 }
377 } else {
378 (String::new(), StringStyle::Double, 0)
379 };
380
381 let lang = child(node, SyntaxKind::Langtag).map(|n| {
382 let span = text_range(&n);
383 let text = terminal_text(&n);
384 let value = text.strip_prefix('@').unwrap_or(&text).to_string();
385 Spanned(value, span)
386 });
387
388 let ty = child(node, SyntaxKind::Iri).map(|n| Spanned(convert_iri(&n), text_range(&n)));
389
390 RDFLiteral {
391 value,
392 quote_style,
393 lang,
394 ty,
395 idx: offset,
396 len,
397 }
398}
399
400fn strip_string_delimiters(text: &str) -> (&str, StringStyle) {
401 if let Some(inner) = text
402 .strip_prefix("\"\"\"")
403 .and_then(|s| s.strip_suffix("\"\"\""))
404 {
405 (inner, StringStyle::DoubleLong)
406 } else if let Some(inner) = text.strip_prefix("'''").and_then(|s| s.strip_suffix("'''")) {
407 (inner, StringStyle::SingleLong)
408 } else if let Some(inner) = text.strip_prefix('"').and_then(|s| s.strip_suffix('"')) {
409 (inner, StringStyle::Double)
410 } else if let Some(inner) = text.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) {
411 (inner, StringStyle::Single)
412 } else {
413 (text, StringStyle::Double)
414 }
415}
416
417#[cfg(test)]
418mod tests {
419 use super::*;
420 use crate::{
421 IncrementalBias, PrevParseInfo, TokenTrait, parse as crate_parse, parse_incremental,
422 trig::parser as lang,
423 };
424
425 fn parse(input: &str) -> Turtle {
426 let (result, _) = crate_parse(lang::Rule::new(lang::SyntaxKind::TrigDoc), input);
427 let root = result.syntax::<lang::Lang>();
428 println!("{:#?}", root);
429 convert(&root)
430 }
431
432 fn prev_info(text: &str) -> PrevParseInfo {
433 let (_, tokens) = crate_parse(lang::Rule::new(lang::SyntaxKind::TrigDoc), text);
434 let mut depth: i32 = 0;
435 PrevParseInfo {
436 tokens: tokens
437 .iter()
438 .map(|t| {
439 let d = depth.clamp(0, 255) as u8;
440 depth += t.kind.bracket_delta() as i32;
441 t.to_prev_token(d)
442 })
443 .collect(),
444 had_errors: false,
445 }
446 }
447
448 fn parse_incr(before: &str, after: &str) -> Turtle {
449 let prev = prev_info(before);
450 let bias = IncrementalBias::default();
451 let (result, _) = parse_incremental(
452 lang::Rule::new(lang::SyntaxKind::TrigDoc),
453 after,
454 Some(&prev),
455 bias,
456 );
457 let root = result.syntax::<lang::Lang>();
458 convert(&root)
459 }
460
461 fn prefixed(prefix: &str, value: &str) -> NamedNode {
464 NamedNode::Prefixed {
465 prefix: prefix.to_string(),
466 value: value.to_string(),
467 idx: 0,
468 computed: None,
469 }
470 }
471
472 fn nn_eq(a: &NamedNode, b: &NamedNode) -> bool {
473 match (a, b) {
474 (NamedNode::Full(s1, _), NamedNode::Full(s2, _)) => s1 == s2,
475 (
476 NamedNode::Prefixed {
477 prefix: p1,
478 value: v1,
479 ..
480 },
481 NamedNode::Prefixed {
482 prefix: p2,
483 value: v2,
484 ..
485 },
486 ) => p1 == p2 && v1 == v2,
487 (NamedNode::A(_), NamedNode::A(_)) => true,
488 (NamedNode::Invalid, NamedNode::Invalid) => true,
489 _ => false,
490 }
491 }
492
493 fn term_nn(t: &Term) -> &NamedNode {
494 match t {
495 Term::NamedNode(nn) => nn,
496 other => panic!("expected NamedNode, got {:?}", other),
497 }
498 }
499
500 fn term_lit(t: &Term) -> &Literal {
501 match t {
502 Term::Literal(l) => l,
503 other => panic!("expected Literal, got {:?}", other),
504 }
505 }
506
507 fn term_bn(t: &Term) -> &BlankNode {
508 match t {
509 Term::BlankNode(bn) => bn,
510 other => panic!("expected BlankNode, got {:?}", other),
511 }
512 }
513
514 #[test]
517 fn test_prefix_directive() {
518 let doc = parse("@prefix ex: <http://example.org/> .");
519 assert_eq!(doc.prefixes.len(), 1);
520 let p = doc.prefixes[0].value();
521 assert_eq!(p.prefix.value(), "ex");
522 assert!(nn_eq(
523 p.value.value(),
524 &NamedNode::Full("http://example.org/".to_string(), 0)
525 ));
526 }
527
528 #[test]
529 fn test_base_directive() {
530 let doc = parse("@base <http://example.org/> .");
531 let base = doc.base.as_ref().expect("base should be present");
532 assert!(nn_eq(
533 base.value().1.value(),
534 &NamedNode::Full("http://example.org/".to_string(), 0)
535 ));
536 }
537
538 #[test]
541 fn test_simple_triple() {
542 let doc = parse("@prefix ex: <http://example.org/> . ex:alice ex:knows ex:bob .");
543 assert_eq!(doc.triples.len(), 1);
544 let t = doc.triples[0].value();
545 assert!(nn_eq(term_nn(t.subject.value()), &prefixed("ex", "alice")));
546 assert!(nn_eq(
547 term_nn(t.po[0].object[0].value()),
548 &prefixed("ex", "bob")
549 ));
550 assert!(t.graph.is_none());
551 }
552
553 #[test]
554 fn test_full_iri_triple() {
555 let doc = parse(
556 "<http://example.org/alice> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://xmlns.com/foaf/0.1/Person> .",
557 );
558 assert_eq!(doc.triples.len(), 1);
559 let t = doc.triples[0].value();
560 assert!(nn_eq(
561 term_nn(t.subject.value()),
562 &NamedNode::Full("http://example.org/alice".to_string(), 0)
563 ));
564 }
565
566 #[test]
567 fn test_a_shorthand() {
568 let doc = parse(
569 "@prefix foaf: <http://xmlns.com/foaf/0.1/> . <http://example.org/alice> a foaf:Person .",
570 );
571 let t = doc.triples[0].value();
572 assert!(matches!(
573 t.po[0].predicate.value(),
574 Term::NamedNode(NamedNode::A(_))
575 ));
576 }
577
578 #[test]
579 fn test_multiple_po_pairs() {
580 let doc =
581 parse("@prefix ex: <http://example.org/> . ex:alice ex:name \"Alice\" ; ex:age 30 .");
582 let t = doc.triples[0].value();
583 assert_eq!(t.po.len(), 2);
584 }
585
586 #[test]
587 fn test_multiple_objects() {
588 let doc =
589 parse("@prefix ex: <http://example.org/> . ex:alice ex:knows ex:bob , ex:carol .");
590 let t = doc.triples[0].value();
591 assert_eq!(t.po[0].object.len(), 2);
592 }
593
594 #[test]
595 fn test_string_literal() {
596 let doc = parse("@prefix ex: <http://example.org/> . ex:alice ex:name \"Alice\" .");
597 let t = doc.triples[0].value();
598 let lit = term_lit(t.po[0].object[0].value());
599 assert_eq!(lit.plain_string(), "Alice");
600 }
601
602 #[test]
603 fn test_string_literal_with_lang() {
604 let doc = parse("@prefix ex: <http://example.org/> . ex:alice ex:name \"Alice\"@en .");
605 let t = doc.triples[0].value();
606 match term_lit(t.po[0].object[0].value()) {
607 Literal::RDF(r) => {
608 assert_eq!(r.value, "Alice");
609 assert_eq!(r.lang.as_ref().map(|l| l.as_str()), Some("en"));
610 }
611 other => panic!("expected RDF literal, got {:?}", other),
612 }
613 }
614
615 #[test]
616 fn test_numeric_literal() {
617 let doc = parse("@prefix ex: <http://example.org/> . ex:alice ex:age 30 .");
618 let t = doc.triples[0].value();
619 match term_lit(t.po[0].object[0].value()) {
620 Literal::Numeric(n) => assert_eq!(n, "30"),
621 other => panic!("expected Numeric literal, got {:?}", other),
622 }
623 }
624
625 #[test]
626 fn test_boolean_literal() {
627 let doc = parse("@prefix ex: <http://example.org/> . ex:alice ex:active true .");
628 println!("{:?}", doc);
629 let t = doc.triples[0].value();
630 assert_eq!(*term_lit(t.po[0].object[0].value()), Literal::Boolean(true));
631 }
632
633 #[test]
634 fn test_named_blank_node() {
635 let doc = parse("@prefix ex: <http://example.org/> . _:b0 ex:knows ex:alice .");
636 let t = doc.triples[0].value();
637 match term_bn(t.subject.value()) {
638 BlankNode::Named(name, _) => assert_eq!(name, "b0"),
639 other => panic!("expected Named blank node, got {:?}", other),
640 }
641 }
642
643 #[test]
644 fn test_anon_blank_node_property_list() {
645 let doc =
646 parse("@prefix ex: <http://example.org/> . ex:alice ex:knows [ ex:name \"Bob\" ] .");
647 let t = doc.triples[0].value();
648 match term_bn(t.po[0].object[0].value()) {
649 BlankNode::Unnamed(pos, _, _) => {
650 assert_eq!(pos.len(), 1);
651 assert!(nn_eq(
652 term_nn(pos[0].predicate.value()),
653 &prefixed("ex", "name")
654 ));
655 }
656 other => panic!("expected Unnamed blank node, got {:?}", other),
657 }
658 }
659
660 #[test]
661 fn test_collection() {
662 let doc =
663 parse("@prefix ex: <http://example.org/> . ex:alice ex:list ( ex:a ex:b ex:c ) .");
664 let t = doc.triples[0].value();
665 match t.po[0].object[0].value() {
666 Term::Collection(items) => {
667 assert_eq!(items.len(), 3);
668 assert!(nn_eq(term_nn(items[0].value()), &prefixed("ex", "a")));
669 assert!(nn_eq(term_nn(items[1].value()), &prefixed("ex", "b")));
670 assert!(nn_eq(term_nn(items[2].value()), &prefixed("ex", "c")));
671 }
672 other => panic!("expected Collection, got {:?}", other),
673 }
674 }
675
676 #[test]
679 fn test_default_graph_wrapped() {
680 let doc = parse("@prefix ex: <http://example.org/> . { ex:alice ex:knows ex:bob . }");
681 assert_eq!(doc.triples.len(), 1);
682 let t = doc.triples[0].value();
683 assert!(nn_eq(term_nn(t.subject.value()), &prefixed("ex", "alice")));
684 assert!(t.graph.is_none());
685 }
686
687 #[test]
688 fn test_default_graph_multiple_triples() {
689 let doc = parse(
690 "@prefix ex: <http://example.org/> . { ex:alice ex:knows ex:bob . ex:bob ex:knows ex:carol . }",
691 );
692 assert_eq!(doc.triples.len(), 2);
693 assert!(doc.triples[0].value().graph.is_none());
694 assert!(doc.triples[1].value().graph.is_none());
695 }
696
697 #[test]
700 fn test_named_graph_with_keyword() {
701 let doc =
702 parse("@prefix ex: <http://example.org/> . GRAPH ex:g1 { ex:alice ex:knows ex:bob . }");
703 assert_eq!(doc.triples.len(), 1);
704 let t = doc.triples[0].value();
705 assert!(nn_eq(term_nn(t.subject.value()), &prefixed("ex", "alice")));
706 let graph = t.graph.as_ref().expect("should have graph");
707 assert!(nn_eq(term_nn(graph.value()), &prefixed("ex", "g1")));
708 }
709
710 #[test]
711 fn test_named_graph_multiple_triples() {
712 let doc = parse(
713 "@prefix ex: <http://example.org/> . GRAPH ex:g1 { ex:alice ex:knows ex:bob . ex:bob ex:knows ex:carol . }",
714 );
715 assert_eq!(doc.triples.len(), 2);
716 for t in &doc.triples {
717 let graph = t.value().graph.as_ref().expect("should have graph");
718 assert!(nn_eq(term_nn(graph.value()), &prefixed("ex", "g1")));
719 }
720 }
721
722 #[test]
723 fn test_named_graph_full_iri() {
724 let doc = parse(
725 "GRAPH <http://example.org/g1> { <http://example.org/alice> <http://example.org/knows> <http://example.org/bob> . }",
726 );
727 assert_eq!(doc.triples.len(), 1);
728 let t = doc.triples[0].value();
729 let graph = t.graph.as_ref().expect("should have graph");
730 assert!(nn_eq(
731 term_nn(graph.value()),
732 &NamedNode::Full("http://example.org/g1".to_string(), 0)
733 ));
734 }
735
736 #[test]
739 fn test_named_graph_without_keyword() {
740 let doc = parse("@prefix ex: <http://example.org/> . ex:g1 { ex:alice ex:knows ex:bob . }");
741 assert_eq!(doc.triples.len(), 1);
742 let t = doc.triples[0].value();
743 assert!(nn_eq(term_nn(t.subject.value()), &prefixed("ex", "alice")));
744 let graph = t.graph.as_ref().expect("should have graph");
745 assert!(nn_eq(term_nn(graph.value()), &prefixed("ex", "g1")));
746 }
747
748 #[test]
751 fn test_triples2_blank_node_property_list() {
752 let doc = parse("@prefix ex: <http://example.org/> . [ ex:name \"Alice\" ] ex:age 30 .");
753 assert_eq!(doc.triples.len(), 1);
754 let t = doc.triples[0].value();
755 assert!(matches!(
756 t.subject.value(),
757 Term::BlankNode(BlankNode::Unnamed(_, _, _))
758 ));
759 assert_eq!(t.po.len(), 1);
760 }
761
762 #[test]
765 fn test_sparql_prefix_directive() {
766 let doc = parse("PREFIX ex: <http://example.org/> ex:alice ex:knows ex:bob .");
767 assert_eq!(doc.prefixes.len(), 1);
768 assert_eq!(doc.prefixes[0].value().prefix.value(), "ex");
769 }
770
771 #[test]
772 fn test_sparql_base_directive() {
773 let doc = parse("BASE <http://example.org/> <alice> <knows> <bob> .");
774 let base = doc.base.as_ref().expect("base should be present");
775 assert!(nn_eq(
776 base.value().1.value(),
777 &NamedNode::Full("http://example.org/".to_string(), 0)
778 ));
779 }
780
781 #[test]
784 fn test_string_literal_with_datatype() {
785 let doc = parse(
786 "@prefix xsd: <http://www.w3.org/2001/XMLSchema#> . @prefix ex: <http://example.org/> . ex:alice ex:age \"30\"^^xsd:integer .",
787 );
788 let t = doc.triples[0].value();
789 match term_lit(t.po[0].object[0].value()) {
790 Literal::RDF(r) => {
791 assert_eq!(r.value, "30");
792 assert!(r.ty.is_some());
793 assert!(nn_eq(r.ty.as_ref().unwrap(), &prefixed("xsd", "integer")));
794 }
795 other => panic!("expected RDF literal, got {:?}", other),
796 }
797 }
798
799 #[test]
802 fn test_multiple_triples() {
803 let doc =
804 parse("@prefix ex: <http://example.org/> . ex:alice ex:age 30 . ex:bob ex:age 25 .");
805 assert_eq!(doc.triples.len(), 2);
806 assert!(nn_eq(
807 term_nn(doc.triples[0].value().subject.value()),
808 &prefixed("ex", "alice")
809 ));
810 assert!(nn_eq(
811 term_nn(doc.triples[1].value().subject.value()),
812 &prefixed("ex", "bob")
813 ));
814 }
815
816 fn parse_raw(input: &str) -> crate::Parse {
819 let (result, _) = crate_parse(lang::Rule::new(lang::SyntaxKind::TrigDoc), input);
820 result
821 }
822
823 #[test]
824 fn test_valid_input_has_no_errors() {
825 let p = parse_raw("@prefix ex: <http://example.org/> . ex:alice ex:age 30 .");
826 assert_eq!(p.errors.len(), 0, "valid input should produce no errors");
827 }
828
829 #[test]
830 fn test_missing_trailing_dot_reports_error() {
831 let p = parse_raw("@prefix ex: <http://example.org/> . ex:alice ex:age 30");
832 assert!(
833 p.errors.len() > 0,
834 "missing trailing dot should produce an error"
835 );
836 assert!(
837 p.errors.iter().any(|e| e.contains("Stop")),
838 "expected an error mentioning Stop, got: {:?}",
839 p.errors.iter().collect::<Vec<_>>()
840 );
841 }
842
843 #[test]
844 fn test_missing_prefix_iri_reports_error() {
845 let p = parse_raw("@prefix ex: .");
846 assert!(
847 p.errors.len() > 0,
848 "missing prefix IRI should produce an error"
849 );
850 }
851
852 #[test]
861 fn test_missing_open_curly_error_span_precedes_close_curly() {
862 let input = " <a <a> <b> <c> }";
863
864 let p = parse_raw(input);
865 assert!(
868 p.errors.len() >= 1,
869 "expected at least one error, got: {:?}",
870 p.errors.iter().collect::<Vec<_>>()
871 );
872
873 let root = p.syntax::<lang::Lang>();
874
875 let error_node = root
876 .descendants_with_tokens()
877 .filter_map(|e| e.into_node())
878 .find(|n| n.kind() == lang::SyntaxKind::Error)
879 .expect("expected an Error node in the CST");
880
881 let close_curly_offset: usize = root
882 .descendants_with_tokens()
883 .filter(|t| t.kind() == lang::SyntaxKind::CurlyClose)
884 .map(|t| usize::from(t.text_range().start()))
885 .next()
886 .expect("expected a CurlyClose token in the CST");
887
888 assert!(
889 usize::from(error_node.text_range().start()) < close_curly_offset,
890 "error should precede `}}` in the CST"
891 );
892
893 let span = crate::effective_error_span::<lang::Lang>(&error_node);
894 assert_eq!(
895 span.len(),
896 5,
897 "effective span should cover the 2 leading whitespace bytes, got {:?}",
898 span
899 );
900 }
901
902 #[test]
905 fn test_mixed_default_and_named() {
906 let doc = parse(
907 "@prefix ex: <http://example.org/> .\n\
908 ex:s1 ex:p1 ex:o1 .\n\
909 GRAPH ex:g1 { ex:s2 ex:p2 ex:o2 . }",
910 );
911 assert_eq!(doc.triples.len(), 2);
912 assert!(doc.triples[0].value().graph.is_none());
913 assert!(doc.triples[1].value().graph.is_some());
914 }
915
916 #[test]
917 fn test_empty_wrapped_graph() {
918 let doc = parse("@prefix ex: <http://example.org/> . GRAPH ex:g1 { }");
919 assert_eq!(doc.triples.len(), 0);
920 }
921
922 #[test]
923 fn test_empty_default_graph() {
924 let doc = parse("{ }");
925 assert_eq!(doc.triples.len(), 0);
926 }
927
928 #[test]
929 fn test_named_graph_blank_node_graph_name() {
930 let doc =
931 parse("@prefix ex: <http://example.org/> . GRAPH _:g1 { ex:alice ex:knows ex:bob . }");
932 assert_eq!(doc.triples.len(), 1);
933 let graph = doc.triples[0]
934 .value()
935 .graph
936 .as_ref()
937 .expect("should have graph");
938 match graph.value() {
939 Term::BlankNode(BlankNode::Named(name, _)) => assert_eq!(name, "g1"),
940 other => panic!("expected Named blank node graph, got {:?}", other),
941 }
942 }
943
944 #[test]
952 fn test_incremental_remove_curly_braces() {
953 let before = "{ <b> <c> <d> }";
954 let after = "<b> <c> <d> }";
955
956 let (result, _) = parse_incremental(
957 lang::Rule::new(lang::SyntaxKind::TrigDoc),
958 after,
959 Some(&prev_info(before)),
960 IncrementalBias::default(),
961 );
962 assert!(
963 result.errors.len() == 1,
964 "expected one parse errors, got: {:?}",
965 result.errors.iter().collect::<Vec<_>>()
966 );
967
968 let root = result.syntax::<lang::Lang>();
969 println!("{:#?}", root);
970 let doc = convert(&root);
971
972 assert_eq!(doc.triples.len(), 1, "should produce exactly one triple");
973
974 let t = doc.triples[0].value();
975 assert!(t.graph.is_none(), "triple should be in the default graph");
976
977 match t.subject.value() {
978 Term::NamedNode(NamedNode::Full(iri, _)) => assert_eq!(iri, "b"),
979 other => panic!("expected <b> as subject, got {:?}", other),
980 }
981 match t.po[0].value().predicate.value() {
982 Term::NamedNode(NamedNode::Full(iri, _)) => assert_eq!(iri, "c"),
983 other => panic!("expected <c> as predicate, got {:?}", other),
984 }
985 match t.po[0].value().object[0].value() {
986 Term::NamedNode(NamedNode::Full(iri, _)) => assert_eq!(iri, "d"),
987 other => panic!("expected <d> as object, got {:?}", other),
988 }
989 }
990
991 #[test]
995 fn test_incremental_remove_graph_iri() {
996 let before = "<a> { <b> <c> <d> }";
997 let after = "{ <b> <c> <d> }";
998
999 let doc = parse_incr(before, after);
1000
1001 assert_eq!(doc.triples.len(), 1, "should produce exactly one triple");
1002
1003 let t = doc.triples[0].value();
1004 assert!(t.graph.is_none(), "triple should be in the default graph");
1005
1006 match t.subject.value() {
1007 Term::NamedNode(NamedNode::Full(iri, _)) => assert_eq!(iri, "b"),
1008 other => panic!("expected <b> as subject, got {:?}", other),
1009 }
1010 match t.po[0].value().predicate.value() {
1011 Term::NamedNode(NamedNode::Full(iri, _)) => assert_eq!(iri, "c"),
1012 other => panic!("expected <c> as predicate, got {:?}", other),
1013 }
1014 match t.po[0].value().object[0].value() {
1015 Term::NamedNode(NamedNode::Full(iri, _)) => assert_eq!(iri, "d"),
1016 other => panic!("expected <d> as object, got {:?}", other),
1017 }
1018 }
1019
1020 #[test]
1024 fn test_incremental_add_graph_iri() {
1025 let doc = parse_incr("{ <b> <c> <d> }", "<g> { <b> <c> <d> }");
1026
1027 assert_eq!(doc.triples.len(), 1, "should produce exactly one triple");
1028
1029 let t = doc.triples[0].value();
1030 match t.graph.as_ref().map(|g| g.value()) {
1031 Some(Term::NamedNode(NamedNode::Full(iri, _))) => assert_eq!(iri, "g"),
1032 other => panic!("expected <g> as graph name, got {:?}", other),
1033 }
1034 match t.subject.value() {
1035 Term::NamedNode(NamedNode::Full(iri, _)) => assert_eq!(iri, "b"),
1036 other => panic!("expected <b> as subject, got {:?}", other),
1037 }
1038 }
1039
1040 #[test]
1044 fn test_incremental_change_object_inside_graph() {
1045 let doc = parse_incr("{ <b> <c> <d> }", "{ <b> <c> <e> }");
1046
1047 assert_eq!(doc.triples.len(), 1, "should produce exactly one triple");
1048
1049 let t = doc.triples[0].value();
1050 assert!(t.graph.is_none(), "triple should be in the default graph");
1051 match t.po[0].value().object[0].value() {
1052 Term::NamedNode(NamedNode::Full(iri, _)) => assert_eq!(iri, "e"),
1053 other => panic!("expected <e> as object, got {:?}", other),
1054 }
1055 }
1056
1057 #[test]
1062 fn test_incremental_add_triple_inside_named_graph() {
1063 let doc = parse_incr("<g> { <b> <c> <d> }", "<g> { <b> <c> <d> . <e> <f> <h> }");
1064
1065 assert_eq!(doc.triples.len(), 2, "should produce two triples");
1066
1067 for t in doc.triples.iter() {
1068 match t.value().graph.as_ref().map(|g| g.value()) {
1069 Some(Term::NamedNode(NamedNode::Full(iri, _))) => assert_eq!(iri, "g"),
1070 other => panic!("expected <g> as graph for both triples, got {:?}", other),
1071 }
1072 }
1073 match doc.triples[0].value().subject.value() {
1074 Term::NamedNode(NamedNode::Full(iri, _)) => assert_eq!(iri, "b"),
1075 other => panic!("expected <b> as first subject, got {:?}", other),
1076 }
1077 match doc.triples[1].value().subject.value() {
1078 Term::NamedNode(NamedNode::Full(iri, _)) => assert_eq!(iri, "e"),
1079 other => panic!("expected <e> as second subject, got {:?}", other),
1080 }
1081 }
1082
1083 #[test]
1087 fn test_incremental_remove_triple_inside_named_graph() {
1088 let doc = parse_incr("<g> { <b> <c> <d> . <e> <f> <h> }", "<g> { <b> <c> <d> }");
1089
1090 assert_eq!(doc.triples.len(), 1, "should produce exactly one triple");
1091
1092 let t = doc.triples[0].value();
1093 match t.graph.as_ref().map(|g| g.value()) {
1094 Some(Term::NamedNode(NamedNode::Full(iri, _))) => assert_eq!(iri, "g"),
1095 other => panic!("expected <g> as graph, got {:?}", other),
1096 }
1097 match t.subject.value() {
1098 Term::NamedNode(NamedNode::Full(iri, _)) => assert_eq!(iri, "b"),
1099 other => panic!("expected <b> as subject, got {:?}", other),
1100 }
1101 }
1102}