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
27pub fn convert(root: &Node) -> Turtle {
28 let mut base = None;
29 let mut prefixes = Vec::new();
30 let mut triples = Vec::new();
31
32 for child_node in root.children() {
35 let kind = child_node.kind();
36 match kind {
37 SyntaxKind::N3Statement => {
38 if let Some(dir) = child(&child_node, SyntaxKind::N3Directive) {
39 if let Some(b) = child(&dir, SyntaxKind::Base) {
40 base = Some(Spanned(convert_base(&b), text_range(&b)));
41 } else if let Some(p) = child(&dir, SyntaxKind::PrefixId) {
42 prefixes.push(Spanned(convert_prefix(&p), text_range(&p)));
43 }
44 } else if let Some(t) = child(&child_node, SyntaxKind::Triples) {
45 collect_nested_triples(&t, &mut triples);
47 let range = text_range(&child_node);
50 let triple = convert_triples(&t);
51 if !matches!(triple.subject.value(), Term::Invalid) {
52 triples.push(Spanned(triple, range));
53 }
54 }
55 }
56 SyntaxKind::SparqlDirective => {
57 if let Some(b) = child(&child_node, SyntaxKind::SparqlBase) {
58 base = Some(Spanned(convert_base(&b), text_range(&b)));
59 } else if let Some(p) = child(&child_node, SyntaxKind::SparqlPrefix) {
60 prefixes.push(Spanned(convert_prefix(&p), text_range(&p)));
61 }
62 }
63 _ => {}
64 }
65 }
66
67 Turtle::new(base, prefixes, triples)
68}
69
70fn collect_nested_triples(node: &Node, triples: &mut Vec<Spanned<Triple>>) {
72 for c in node.children() {
73 let kind = c.kind();
74 if kind == SyntaxKind::Formula || kind == SyntaxKind::FormulaContent {
75 collect_all_triples(&c, triples);
76 } else {
77 collect_nested_triples(&c, triples);
78 }
79 }
80}
81
82fn collect_all_triples(node: &Node, triples: &mut Vec<Spanned<Triple>>) {
84 for c in node.children() {
85 if c.kind() == SyntaxKind::Triples {
86 let range = text_range(&c);
87 triples.push(Spanned(convert_triples(&c), range));
88 }
89 collect_all_triples(&c, triples);
91 }
92}
93
94fn convert_base(node: &Node) -> Base {
97 let range = text_range(node);
98 let nn = child(node, SyntaxKind::Iriref)
99 .map(|n| iri_from_iriref_node(&n))
100 .or_else(|| child(node, SyntaxKind::Iri).map(|n| convert_iri(&n)))
101 .unwrap_or(NamedNode::Invalid);
102
103 let nn_range = child(node, SyntaxKind::Iriref)
104 .or_else(|| child(node, SyntaxKind::Iri))
105 .map(|n| text_range(&n))
106 .unwrap_or(range.clone());
107
108 Base(range, Spanned(nn, nn_range))
109}
110
111fn convert_prefix(node: &Node) -> TurtlePrefix {
112 let range = text_range(node);
113
114 let prefix_text = child(node, SyntaxKind::PnameNs)
115 .map(|n| {
116 let text = terminal_text(&n);
117 let tr = text_range(&n);
118 Spanned(text.trim_end_matches(':').to_string(), tr)
119 })
120 .unwrap_or_else(|| Spanned(String::new(), range.clone()));
121
122 let value = child(node, SyntaxKind::Iriref)
123 .map(|n| {
124 let tr = text_range(&n);
125 Spanned(iri_from_iriref_node(&n), tr)
126 })
127 .or_else(|| {
128 child(node, SyntaxKind::Iri).map(|n| {
129 let r = text_range(&n);
130 Spanned(convert_iri(&n), r)
131 })
132 })
133 .unwrap_or_else(|| Spanned(NamedNode::Invalid, range.clone()));
134
135 TurtlePrefix {
136 span: range,
137 prefix: prefix_text,
138 value,
139 }
140}
141
142fn convert_triples(node: &Node) -> Triple {
145 let subject = if let Some(subj) = child(node, SyntaxKind::Subject) {
147 let range = text_range(&subj);
148 Spanned(convert_subject(&subj), range)
149 } else {
150 Spanned(Term::Invalid, text_range(node))
151 };
152
153 let po = child(node, SyntaxKind::PredicateObjectList)
154 .map(|n| convert_predicate_object_list(&n))
155 .unwrap_or_default();
156
157 Triple {
158 subject,
159 po,
160 graph: None,
161 }
162}
163
164fn convert_predicate_object_list(node: &Node) -> Vec<Spanned<PO>> {
165 let verbs: Vec<_> = children(node, SyntaxKind::Verb).collect();
166 let object_lists: Vec<_> = children(node, SyntaxKind::ObjectList).collect();
167
168 verbs
169 .into_iter()
170 .zip(object_lists.into_iter())
171 .map(|(v, ol)| {
172 let range = text_range(&v).start..text_range(&ol).end;
173 let pred_range = text_range(&v);
174 let predicate = convert_verb(&v);
175 let objects = convert_object_list(&ol);
176 Spanned(
177 PO {
178 predicate: Spanned(predicate, pred_range),
179 object: objects,
180 },
181 range,
182 )
183 })
184 .collect()
185}
186
187fn convert_verb(node: &Node) -> Term {
188 if let Some(pred) = child(node, SyntaxKind::Predicate) {
190 convert_predicate(&pred)
191 } else if let Some(alit) = child(node, SyntaxKind::Alit) {
192 Term::NamedNode(NamedNode::A(alit.text_range().start().into()))
193 } else if child(node, SyntaxKind::HasLit).is_some() {
194 if let Some(expr) = child(node, SyntaxKind::Expression) {
196 convert_expression(&expr)
197 } else {
198 Term::Invalid
199 }
200 } else if child(node, SyntaxKind::IsLit).is_some() {
201 if let Some(expr) = child(node, SyntaxKind::Expression) {
203 convert_expression(&expr)
204 } else {
205 Term::Invalid
206 }
207 } else if child(node, SyntaxKind::Eq).is_some() {
208 Term::NamedNode(NamedNode::Full(
209 "http://www.w3.org/2002/07/owl#sameAs".to_string(),
210 node.text_range().start().into(),
211 ))
212 } else if child(node, SyntaxKind::ImplyLeft).is_some() {
213 Term::NamedNode(NamedNode::Full(
214 "http://www.w3.org/2000/10/swap/log#impliedBy".to_string(),
215 node.text_range().start().into(),
216 ))
217 } else if child(node, SyntaxKind::ImplyRight).is_some() {
218 Term::NamedNode(NamedNode::Full(
219 "http://www.w3.org/2000/10/swap/log#implies".to_string(),
220 node.text_range().start().into(),
221 ))
222 } else {
223 Term::Invalid
224 }
225}
226
227fn convert_predicate(node: &Node) -> Term {
228 if let Some(expr) = child(node, SyntaxKind::Expression) {
231 convert_expression(&expr)
232 } else {
233 Term::Invalid
234 }
235}
236
237fn convert_object_list(node: &Node) -> Vec<Spanned<Term>> {
238 children(node, SyntaxKind::Object)
239 .map(|o| {
240 let range = text_range(&o);
241 Spanned(convert_object(&o), range)
242 })
243 .collect()
244}
245
246fn convert_subject(node: &Node) -> Term {
247 if let Some(expr) = child(node, SyntaxKind::Expression) {
249 convert_expression(&expr)
250 } else {
251 Term::Invalid
252 }
253}
254
255fn convert_object(node: &Node) -> Term {
256 if let Some(expr) = child(node, SyntaxKind::Expression) {
258 convert_expression(&expr)
259 } else {
260 Term::Invalid
261 }
262}
263
264fn convert_expression(node: &Node) -> Term {
267 child(node, SyntaxKind::Path)
268 .map(|p| convert_path(&p))
269 .unwrap_or(Term::Invalid)
270}
271
272fn convert_path(node: &Node) -> Term {
273 child(node, SyntaxKind::PathItem)
276 .map(|pi| convert_path_item(&pi))
277 .unwrap_or(Term::Invalid)
278}
279
280fn convert_path_item(node: &Node) -> Term {
281 if let Some(iri) = child(node, SyntaxKind::Iri) {
282 Term::NamedNode(convert_iri(&iri))
283 } else if let Some(bn) = child(node, SyntaxKind::BlankNode) {
284 Term::BlankNode(convert_blank_node(&bn))
285 } else if let Some(qv) = child(node, SyntaxKind::QuickVar) {
286 convert_quick_var(&qv)
287 } else if let Some(coll) = child(node, SyntaxKind::Collection) {
288 Term::Collection(convert_collection(&coll))
289 } else if let Some(bpl) = child(node, SyntaxKind::BlankNodePropertyList) {
290 convert_blank_node_property_list(&bpl)
291 } else if let Some(ipl) = child(node, SyntaxKind::IriPropertyList) {
292 convert_iri_property_list(&ipl)
293 } else if let Some(lit) = child(node, SyntaxKind::Literal) {
294 Term::Literal(convert_literal(&lit))
295 } else if child(node, SyntaxKind::Formula).is_some() {
296 Term::Invalid
298 } else {
299 Term::Invalid
300 }
301}
302
303fn convert_iri(node: &Node) -> NamedNode {
306 if let Some(iriref_node) = child(node, SyntaxKind::Iriref) {
307 iri_from_iriref_node(&iriref_node)
308 } else if let Some(pn) = child(node, SyntaxKind::PrefixedName) {
309 convert_prefixed_name(&pn)
310 } else {
311 NamedNode::Invalid
312 }
313}
314
315fn iri_from_iriref_node(node: &Node) -> NamedNode {
316 let text = terminal_text(node);
317 let offset: usize = node.text_range().start().into();
318 let inner = text.trim_start_matches('<').trim_end_matches('>');
319 NamedNode::Full(inner.to_string(), offset)
320}
321
322fn convert_prefixed_name(node: &Node) -> NamedNode {
323 if let Some(pname_ln) = child(node, SyntaxKind::PnameLn) {
324 let text = terminal_text(&pname_ln);
325 let offset: usize = pname_ln.text_range().start().into();
326 let (prefix, value) = text.split_once(':').unwrap_or((&text, ""));
327 NamedNode::Prefixed {
328 prefix: prefix.to_string(),
329 value: value.to_string(),
330 idx: offset,
331 computed: None,
332 }
333 } else if let Some(pname_ns) = child(node, SyntaxKind::PnameNs) {
334 let text = terminal_text(&pname_ns);
335 let offset: usize = pname_ns.text_range().start().into();
336 let prefix = text.trim_end_matches(':');
337 NamedNode::Prefixed {
338 prefix: prefix.to_string(),
339 value: String::new(),
340 idx: offset,
341 computed: None,
342 }
343 } else {
344 NamedNode::Invalid
345 }
346}
347
348fn convert_blank_node(node: &Node) -> BlankNode {
349 if let Some(label) = child(node, SyntaxKind::BlankNodeLabel) {
350 let text = terminal_text(&label);
351 let offset: usize = label.text_range().start().into();
352 let name = text.strip_prefix("_:").unwrap_or(&text);
353 BlankNode::Named(name.to_string(), offset)
354 } else {
355 let offset: usize = node.text_range().start().into();
356 BlankNode::Unnamed(Vec::new(), offset, offset)
357 }
358}
359
360fn convert_blank_node_property_list(node: &Node) -> Term {
361 let offset: usize = node.text_range().start().into();
362 let end: usize = node.text_range().end().into();
363 let po = child(node, SyntaxKind::PredicateObjectList)
364 .map(|n| convert_predicate_object_list(&n))
365 .unwrap_or_default();
366 Term::BlankNode(BlankNode::Unnamed(po, offset, end))
367}
368
369fn convert_iri_property_list(node: &Node) -> Term {
370 if let Some(iri) = child(node, SyntaxKind::Iri) {
373 Term::NamedNode(convert_iri(&iri))
374 } else {
375 Term::Invalid
376 }
377}
378
379fn convert_quick_var(node: &Node) -> Term {
380 if let Some(qv) = child(node, SyntaxKind::QuickVarName) {
382 let text = terminal_text(&qv);
383 let offset: usize = qv.text_range().start().into();
384 let name = text.strip_prefix('?').unwrap_or(&text);
385 Term::Variable(Variable(name.to_string(), offset))
386 } else {
387 Term::Invalid
388 }
389}
390
391fn convert_collection(node: &Node) -> Vec<Spanned<Term>> {
392 children(node, SyntaxKind::Object)
393 .map(|o| {
394 let range = text_range(&o);
395 Spanned(convert_object(&o), range)
396 })
397 .collect()
398}
399
400fn convert_literal(node: &Node) -> Literal {
401 if let Some(rdf) = child(node, SyntaxKind::RdfLiteral) {
402 Literal::RDF(convert_rdf_literal(&rdf))
403 } else if let Some(num) = child(node, SyntaxKind::NumericLiteral) {
404 Literal::Numeric(num.text().to_string().trim().to_string())
405 } else if let Some(b) = child(node, SyntaxKind::BooleanLiteral) {
406 Literal::Boolean(b.text().to_string().trim() == "true")
407 } else {
408 Literal::Numeric(String::new())
409 }
410}
411
412fn convert_rdf_literal(node: &Node) -> RDFLiteral {
413 let offset: usize = node.text_range().start().into();
416
417 let (value, quote_style, len) = if let Some(str_node) = child(node, SyntaxKind::String) {
418 let text = terminal_text(&str_node);
419 let (inner, style) = strip_string_delimiters(&text);
420 (inner.to_string(), style, 1)
421 } else {
422 (String::new(), StringStyle::Double, 0)
423 };
424
425 let lang = child(node, SyntaxKind::Langtag).map(|n| {
426 let span = text_range(&n);
427 let text = terminal_text(&n);
428 let value = text.strip_prefix('@').unwrap_or(&text).to_string();
429 Spanned(value, span)
430 });
431
432 let ty = child(node, SyntaxKind::Iri).map(|n| Spanned(convert_iri(&n), text_range(&n)));
433
434 RDFLiteral {
435 value,
436 quote_style,
437 lang,
438 ty,
439 idx: offset,
440 len,
441 }
442}
443
444fn strip_string_delimiters(text: &str) -> (&str, StringStyle) {
445 if let Some(inner) = text
446 .strip_prefix("\"\"\"")
447 .and_then(|s| s.strip_suffix("\"\"\""))
448 {
449 (inner, StringStyle::DoubleLong)
450 } else if let Some(inner) = text.strip_prefix("'''").and_then(|s| s.strip_suffix("'''")) {
451 (inner, StringStyle::SingleLong)
452 } else if let Some(inner) = text.strip_prefix('"').and_then(|s| s.strip_suffix('"')) {
453 (inner, StringStyle::Double)
454 } else if let Some(inner) = text.strip_prefix('\'').and_then(|s| s.strip_suffix('\'')) {
455 (inner, StringStyle::Single)
456 } else {
457 (text, StringStyle::Double)
458 }
459}
460
461#[cfg(test)]
462mod tests {
463 use super::*;
464 use crate::{n3::parser as lang, parse as crate_parse};
465
466 fn parse(input: &str) -> Turtle {
467 let (result, _) = crate_parse(lang::Rule::new(lang::SyntaxKind::N3Doc), input);
468 let root = result.syntax::<lang::Lang>();
469 convert(&root)
470 }
471
472 fn prefixed(prefix: &str, value: &str) -> NamedNode {
475 NamedNode::Prefixed {
476 prefix: prefix.to_string(),
477 value: value.to_string(),
478 idx: 0,
479 computed: None,
480 }
481 }
482
483 fn nn_eq(a: &NamedNode, b: &NamedNode) -> bool {
484 match (a, b) {
485 (NamedNode::Full(s1, _), NamedNode::Full(s2, _)) => s1 == s2,
486 (
487 NamedNode::Prefixed {
488 prefix: p1,
489 value: v1,
490 ..
491 },
492 NamedNode::Prefixed {
493 prefix: p2,
494 value: v2,
495 ..
496 },
497 ) => p1 == p2 && v1 == v2,
498 (NamedNode::A(_), NamedNode::A(_)) => true,
499 (NamedNode::Invalid, NamedNode::Invalid) => true,
500 _ => false,
501 }
502 }
503
504 fn term_nn(t: &Term) -> &NamedNode {
505 match t {
506 Term::NamedNode(nn) => nn,
507 other => panic!("expected NamedNode, got {:?}", other),
508 }
509 }
510
511 fn term_lit(t: &Term) -> &Literal {
512 match t {
513 Term::Literal(l) => l,
514 other => panic!("expected Literal, got {:?}", other),
515 }
516 }
517
518 fn term_bn(t: &Term) -> &BlankNode {
519 match t {
520 Term::BlankNode(bn) => bn,
521 other => panic!("expected BlankNode, got {:?}", other),
522 }
523 }
524
525 fn term_var(t: &Term) -> &Variable {
526 match t {
527 Term::Variable(v) => v,
528 other => panic!("expected Variable, got {:?}", other),
529 }
530 }
531
532 #[test]
535 fn test_prefix_directive() {
536 let doc = parse("@prefix ex: <http://example.org/> .");
537 assert_eq!(doc.prefixes.len(), 1);
538 let p = doc.prefixes[0].value();
539 assert_eq!(p.prefix.value(), "ex");
540 assert!(nn_eq(
541 p.value.value(),
542 &NamedNode::Full("http://example.org/".to_string(), 0)
543 ));
544 }
545
546 #[test]
547 fn test_sparql_prefix_directive() {
548 let doc = parse("PREFIX ex: <http://example.org/>\nex:alice ex:knows ex:bob .");
549 assert_eq!(doc.prefixes.len(), 1);
550 assert_eq!(doc.prefixes[0].value().prefix.value(), "ex");
551 }
552
553 #[test]
556 fn test_base_directive() {
557 let doc = parse("@base <http://example.org/> .");
558 let base = doc.base.as_ref().expect("base should be present");
559 assert!(nn_eq(
560 base.value().1.value(),
561 &NamedNode::Full("http://example.org/".to_string(), 0)
562 ));
563 }
564
565 #[test]
566 fn test_sparql_base_directive() {
567 let doc = parse("BASE <http://example.org/>\n<alice> <knows> <bob> .");
568 let base = doc.base.as_ref().expect("base should be present");
569 assert!(nn_eq(
570 base.value().1.value(),
571 &NamedNode::Full("http://example.org/".to_string(), 0)
572 ));
573 }
574
575 #[test]
578 fn test_simple_triple() {
579 let doc = parse("@prefix ex: <http://example.org/> .\nex:alice ex:knows ex:bob .");
580 assert_eq!(doc.triples.len(), 1);
581 let t = doc.triples[0].value();
582 assert!(nn_eq(term_nn(t.subject.value()), &prefixed("ex", "alice")));
583 assert_eq!(t.po.len(), 1);
584 assert!(nn_eq(
585 term_nn(t.po[0].predicate.value()),
586 &prefixed("ex", "knows")
587 ));
588 assert!(nn_eq(
589 term_nn(t.po[0].object[0].value()),
590 &prefixed("ex", "bob")
591 ));
592 }
593
594 #[test]
597 fn test_full_iri_triple() {
598 let doc = parse(
599 "<http://example.org/alice> <http://example.org/knows> <http://example.org/bob> .",
600 );
601 assert_eq!(doc.triples.len(), 1);
602 let t = doc.triples[0].value();
603 assert!(nn_eq(
604 term_nn(t.subject.value()),
605 &NamedNode::Full("http://example.org/alice".to_string(), 0)
606 ));
607 assert!(nn_eq(
608 term_nn(t.po[0].predicate.value()),
609 &NamedNode::Full("http://example.org/knows".to_string(), 0)
610 ));
611 assert!(nn_eq(
612 term_nn(t.po[0].object[0].value()),
613 &NamedNode::Full("http://example.org/bob".to_string(), 0)
614 ));
615 }
616
617 #[test]
620 fn test_a_shorthand() {
621 let doc = parse(
622 "@prefix foaf: <http://xmlns.com/foaf/0.1/> .\n<http://example.org/alice> a foaf:Person .",
623 );
624 let t = doc.triples[0].value();
625 assert!(matches!(
626 t.po[0].predicate.value(),
627 Term::NamedNode(NamedNode::A(_))
628 ));
629 assert!(nn_eq(
630 term_nn(t.po[0].object[0].value()),
631 &prefixed("foaf", "Person")
632 ));
633 }
634
635 #[test]
638 fn test_multiple_po_pairs() {
639 let doc =
640 parse("@prefix ex: <http://example.org/> .\nex:alice ex:name \"Alice\" ; ex:age 30 .");
641 let t = doc.triples[0].value();
642 assert_eq!(t.po.len(), 2);
643 assert!(nn_eq(
644 term_nn(t.po[0].predicate.value()),
645 &prefixed("ex", "name")
646 ));
647 assert!(nn_eq(
648 term_nn(t.po[1].predicate.value()),
649 &prefixed("ex", "age")
650 ));
651 }
652
653 #[test]
656 fn test_multiple_objects() {
657 let doc =
658 parse("@prefix ex: <http://example.org/> .\nex:alice ex:knows ex:bob , ex:carol .");
659 let t = doc.triples[0].value();
660 assert_eq!(t.po[0].object.len(), 2);
661 assert!(nn_eq(
662 term_nn(t.po[0].object[0].value()),
663 &prefixed("ex", "bob")
664 ));
665 assert!(nn_eq(
666 term_nn(t.po[0].object[1].value()),
667 &prefixed("ex", "carol")
668 ));
669 }
670
671 #[test]
674 fn test_string_literal() {
675 let doc = parse("@prefix ex: <http://example.org/> .\nex:alice ex:name \"Alice\" .");
676 let t = doc.triples[0].value();
677 let lit = term_lit(t.po[0].object[0].value());
678 assert_eq!(lit.plain_string(), "Alice");
679 }
680
681 #[test]
684 fn test_numeric_literal() {
685 let doc = parse("@prefix ex: <http://example.org/> .\nex:alice ex:age 30 .");
686 let t = doc.triples[0].value();
687 match term_lit(t.po[0].object[0].value()) {
688 Literal::Numeric(n) => assert_eq!(n, "30"),
689 other => panic!("expected Numeric literal, got {:?}", other),
690 }
691 }
692
693 #[test]
696 fn test_boolean_literal() {
697 let doc = parse("@prefix ex: <http://example.org/> .\nex:alice ex:active true .");
698 let t = doc.triples[0].value();
699 assert_eq!(*term_lit(t.po[0].object[0].value()), Literal::Boolean(true));
700 }
701
702 #[test]
705 fn test_named_blank_node() {
706 let doc = parse("@prefix ex: <http://example.org/> .\n_:b0 ex:knows ex:alice .");
707 let t = doc.triples[0].value();
708 match term_bn(t.subject.value()) {
709 BlankNode::Named(name, _) => assert_eq!(name, "b0"),
710 other => panic!("expected Named blank node, got {:?}", other),
711 }
712 }
713
714 #[test]
717 fn test_anon_blank_node_property_list() {
718 let doc =
719 parse("@prefix ex: <http://example.org/> .\nex:alice ex:knows [ ex:name \"Bob\" ] .");
720 let t = doc.triples[0].value();
721 match term_bn(t.po[0].object[0].value()) {
722 BlankNode::Unnamed(pos, _, _) => {
723 assert_eq!(pos.len(), 1);
724 assert!(nn_eq(
725 term_nn(pos[0].predicate.value()),
726 &prefixed("ex", "name")
727 ));
728 }
729 other => panic!("expected Unnamed blank node, got {:?}", other),
730 }
731 }
732
733 #[test]
736 fn test_string_literal_with_lang() {
737 let doc = parse("@prefix ex: <http://example.org/> .\nex:alice ex:name \"Alice\"@en .");
738 let t = doc.triples[0].value();
739 match term_lit(t.po[0].object[0].value()) {
740 Literal::RDF(r) => {
741 assert_eq!(r.value, "Alice");
742 assert_eq!(r.lang.as_ref().map(|l| l.as_str()), Some("en"));
743 }
744 other => panic!("expected RDF literal, got {:?}", other),
745 }
746 }
747
748 #[test]
749 fn test_string_literal_with_datatype() {
750 let doc = parse(
751 "@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .\n@prefix ex: <http://example.org/> .\nex:alice ex:age \"30\"^^xsd:integer .",
752 );
753 let t = doc.triples[0].value();
754 match term_lit(t.po[0].object[0].value()) {
755 Literal::RDF(r) => {
756 assert_eq!(r.value, "30");
757 assert!(r.ty.is_some());
758 assert!(nn_eq(r.ty.as_ref().unwrap(), &prefixed("xsd", "integer")));
759 }
760 other => panic!("expected RDF literal, got {:?}", other),
761 }
762 }
763
764 #[test]
767 fn test_blank_node_subject_property_list() {
768 let doc = parse("@prefix ex: <http://example.org/> .\n[ ex:name \"Alice\" ] ex:age 30 .");
769 let t = doc.triples[0].value();
770 assert!(matches!(
771 t.subject.value(),
772 Term::BlankNode(BlankNode::Unnamed(_, _, _))
773 ));
774 assert_eq!(t.po.len(), 1);
775 }
776
777 #[test]
780 fn test_multiple_triples() {
781 let doc =
782 parse("@prefix ex: <http://example.org/> .\nex:alice ex:age 30 .\nex:bob ex:age 25 .");
783 assert_eq!(doc.triples.len(), 2);
784 assert!(nn_eq(
785 term_nn(doc.triples[0].value().subject.value()),
786 &prefixed("ex", "alice")
787 ));
788 assert!(nn_eq(
789 term_nn(doc.triples[1].value().subject.value()),
790 &prefixed("ex", "bob")
791 ));
792 }
793
794 fn parse_raw(input: &str) -> crate::Parse {
797 let (result, _) = crate_parse(lang::Rule::new(lang::SyntaxKind::N3Doc), input);
798 result
799 }
800
801 #[test]
802 fn test_valid_input_has_no_errors() {
803 let p = parse_raw("@prefix ex: <http://example.org/> .\nex:alice ex:age 30 .");
804 assert_eq!(p.errors.len(), 0, "valid input should produce no errors");
805 }
806
807 #[test]
808 fn test_trailing_dot_is_optional() {
809 let p = parse_raw("@prefix ex: <http://example.org/> .\nex:alice ex:age 30");
812 assert_eq!(
813 p.errors.len(),
814 0,
815 "N3 trailing dot is optional; should produce no errors"
816 );
817 let doc = parse("@prefix ex: <http://example.org/> .\nex:alice ex:age 30");
818 assert_eq!(doc.triples.len(), 1);
819 }
820
821 #[test]
822 fn test_missing_prefix_iri_reports_error() {
823 let p = parse_raw("@prefix ex: .");
824 assert!(
825 p.errors.len() > 0,
826 "missing prefix IRI should produce an error"
827 );
828 }
829
830 #[test]
833 fn test_quick_variables() {
834 let doc = parse("?x ?y ?z .");
835 assert_eq!(doc.triples.len(), 1);
836 let t = doc.triples[0].value();
837 let v = term_var(t.subject.value());
838 assert_eq!(v.0, "x");
839 let pred_v = term_var(t.po[0].predicate.value());
840 assert_eq!(pred_v.0, "y");
841 let obj_v = term_var(t.po[0].object[0].value());
842 assert_eq!(obj_v.0, "z");
843 }
844
845 #[test]
848 fn test_collection() {
849 let doc =
850 parse("@prefix ex: <http://example.org/> .\nex:alice ex:list ( ex:a ex:b ex:c ) .");
851 let t = doc.triples[0].value();
852 match t.po[0].object[0].value() {
853 Term::Collection(items) => {
854 assert_eq!(items.len(), 3);
855 assert!(nn_eq(term_nn(items[0].value()), &prefixed("ex", "a")));
856 assert!(nn_eq(term_nn(items[1].value()), &prefixed("ex", "b")));
857 assert!(nn_eq(term_nn(items[2].value()), &prefixed("ex", "c")));
858 }
859 other => panic!("expected Collection, got {:?}", other),
860 }
861 }
862
863 #[test]
866 fn test_formula_nested_triples() {
867 let doc =
868 parse("@prefix ex: <http://example.org/> .\n{ ex:a ex:b ex:c } ex:implies ex:d .");
869 assert_eq!(doc.triples.len(), 1);
872
873 let nested = &doc.triples[0];
875 assert!(
876 matches!(nested.value().subject.value(), Term::NamedNode(nn) if nn_eq(nn, &prefixed("ex", "a")))
877 );
878 }
879
880 #[test]
883 fn test_has_verb() {
884 let doc = parse("@prefix ex: <http://example.org/> .\nex:alice has ex:friend ex:bob .");
885 assert_eq!(doc.triples.len(), 1);
886 let t = doc.triples[0].value();
887 assert!(nn_eq(term_nn(t.subject.value()), &prefixed("ex", "alice")));
888 assert!(nn_eq(
890 term_nn(t.po[0].predicate.value()),
891 &prefixed("ex", "friend")
892 ));
893 assert!(nn_eq(
894 term_nn(t.po[0].object[0].value()),
895 &prefixed("ex", "bob")
896 ));
897 }
898
899 #[test]
902 fn test_is_of_verb() {
903 let doc = parse("@prefix ex: <http://example.org/> .\nex:bob is ex:friend of ex:alice .");
904 assert_eq!(doc.triples.len(), 1);
905 let t = doc.triples[0].value();
906 assert!(nn_eq(
908 term_nn(t.po[0].predicate.value()),
909 &prefixed("ex", "friend")
910 ));
911 }
912
913 #[test]
914 fn test_n3_rule_formulas() {
915 let input = "@prefix : <http://example.org/> .\n\n\
916 { ?x a :Human . } => { ?x a :Mortal . } .\n\n\
917 :Socrates a :Human .\n";
918 let doc = parse(input);
919 assert_eq!(doc.triples.len(), 3);
922 assert_eq!(format!("{}", doc.triples[0].value().subject.value()), "?x");
924 assert_eq!(format!("{}", doc.triples[1].value().subject.value()), "?x");
925 assert!(nn_eq(
927 term_nn(doc.triples[2].value().subject.value()),
928 &prefixed("", "Socrates")
929 ));
930 }
931
932 use crate::{IncrementalBias, PrevParseInfo, TokenTrait, parse_incremental};
940
941 fn prev_info_incr(text: &str) -> PrevParseInfo {
942 let (_, tokens) = crate_parse(lang::Rule::new(lang::SyntaxKind::N3Doc), text);
943 let mut depth: i32 = 0;
944 PrevParseInfo {
945 tokens: tokens
946 .iter()
947 .map(|t| {
948 let d = depth.clamp(0, 255) as u8;
949 depth += t.kind.bracket_delta() as i32;
950 t.to_prev_token(d)
951 })
952 .collect(),
953 had_errors: false,
954 }
955 }
956
957 fn parse_incr(before: &str, after: &str) -> Turtle {
958 let prev = prev_info_incr(before);
959 let (result, _) = parse_incremental(
960 lang::Rule::new(lang::SyntaxKind::N3Doc),
961 after,
962 Some(&prev),
963 IncrementalBias::default(),
964 );
965 let root = result.syntax::<lang::Lang>();
966 convert(&root)
967 }
968
969 #[test]
972 fn test_incremental_remove_object() {
973 let doc = parse_incr("<a> <b> <c> .", "<a> <b> .");
974 assert_eq!(doc.triples.len(), 1, "should produce one triple");
975 match doc.triples[0].value().subject.value() {
976 Term::NamedNode(NamedNode::Full(iri, _)) => assert_eq!(iri, "a"),
977 other => panic!("expected <a> as subject, got {:?}", other),
978 }
979 }
980
981 #[test]
984 fn test_incremental_add_triple() {
985 let doc = parse_incr("<a> <b> <c> .", "<a> <b> <c> .\n<d> <e> <f> .");
986 assert_eq!(doc.triples.len(), 2, "should produce two triples");
987 match doc.triples[0].value().subject.value() {
988 Term::NamedNode(NamedNode::Full(iri, _)) => assert_eq!(iri, "a"),
989 other => panic!("expected <a> as first subject, got {:?}", other),
990 }
991 match doc.triples[1].value().subject.value() {
992 Term::NamedNode(NamedNode::Full(iri, _)) => assert_eq!(iri, "d"),
993 other => panic!("expected <d> as second subject, got {:?}", other),
994 }
995 }
996
997 #[test]
1001 fn test_incremental_insert_token_inside_formula() {
1002 let doc = parse_incr("{ <a> <b> <c> . }", "{ <a> <b> <c> <d> . }");
1003 assert!(
1006 doc.triples.len() >= 1,
1007 "should produce at least one triple, got {}",
1008 doc.triples.len()
1009 );
1010 match doc.triples[0].value().subject.value() {
1012 Term::NamedNode(NamedNode::Full(iri, _)) => assert_eq!(iri, "a"),
1013 other => panic!("expected <a> as subject inside formula, got {:?}", other),
1014 }
1015 }
1016
1017 #[test]
1021 fn test_incremental_remove_triple() {
1022 let doc = parse_incr("<a> <b> <c> .\n<d> <e> <f> .", "<a> <b> <c> .");
1023 assert_eq!(doc.triples.len(), 1, "should produce one triple");
1024 match doc.triples[0].value().subject.value() {
1025 Term::NamedNode(NamedNode::Full(iri, _)) => assert_eq!(iri, "a"),
1026 other => panic!("expected <a> as subject, got {:?}", other),
1027 }
1028 }
1029}