Skip to content

Commit

Permalink
labeled bnode using proper rule
Browse files Browse the repository at this point in the history
  • Loading branch information
nbittich committed Nov 23, 2024
1 parent 37f41b4 commit 18ff897
Show file tree
Hide file tree
Showing 2 changed files with 149 additions and 20 deletions.
52 changes: 32 additions & 20 deletions src/triple_common_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -259,10 +259,12 @@ pub(crate) mod literal {
}
}
pub(crate) mod triple {

use crate::grammar::BLANK_NODE_LABEL;
use crate::prelude::*;
use crate::shared::NS_TYPE;
use crate::triple_common_parser::{comments, paren_close, paren_open, BlankNode, Iri};

use std::collections::VecDeque;

pub(crate) fn object_list<'a, F1, F2, T>(
Expand Down Expand Up @@ -353,28 +355,38 @@ pub(crate) mod triple {
),
)
}
// https://www.w3.org/TR/turtle/ 2.6 RDF Blank Nodes
pub(crate) fn labeled_bnode(s: &str) -> ParserResult<BlankNode> {
let parse_label = delimited(
tag(BLANK_NODE_LABEL),
take_while(|s: char| {
!s.is_whitespace()
&& s != '.'
&& s != ';'
&& s != '<'
&& s != '('
&& s != '['
&& s != '"'
}),
space0,
);

map_res(preceded(multispace0, parse_label), |label: &str| {
if label.starts_with('.') || label.ends_with('.') || label.starts_with('-') {
let err: Error<&str> = make_error(label, ErrorKind::IsNot);
return Err(nom::Err::Error(err));
fn allowed_but_not_as_first(c: char) -> bool {
matches!(c,'.' | '-' | '·' | '\u{0300}'..='\u{036F}' | '\u{203F}'..='\u{2040}')
}
let (s, _) = preceded(multispace0, tag(BLANK_NODE_LABEL))(s)?;
let mut idx_bnode = 0;
for c in s.chars() {
if c.is_alphanum() || c == '_' || allowed_but_not_as_first(c) {
idx_bnode += c.len_utf8();
} else {
break;
}
Ok(BlankNode::Labeled(label))
})(s)
}
let mut bnode: &str = &s[0..idx_bnode];
if bnode.ends_with('.') {
idx_bnode -= '.'.len_utf8();
bnode = &s[0..idx_bnode];
}
if bnode.is_empty()
|| bnode
.chars()
.last()
.filter(|c| allowed_but_not_as_first(*c))
.is_some()
|| bnode.chars().take(1).any(allowed_but_not_as_first)
{
let err: Error<&str> = make_error(s, ErrorKind::IsNot);
return Err(nom::Err::Error(err));
}
let rest = &s[idx_bnode..];
Ok((rest, BlankNode::Labeled(bnode)))
}
}
pub(crate) fn comments(s: &str) -> ParserResult<Vec<&str>> {
Expand Down
117 changes: 117 additions & 0 deletions src/turtle/turtle_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -448,6 +448,123 @@ mod test {
);
}

#[test]
fn labeled_bnode_using_proper_rules_test1() {
let s = "_:b.node :a :b";
let (rest, res) = triples(s).unwrap();
assert!(rest.trim().is_empty());
assert_eq!(
res,
TurtleValue::Statement {
subject: Box::new(TurtleValue::BNode(BlankNode::Labeled("b.node",),)),
predicate_objects: [TurtleValue::PredicateObject {
predicate: Box::new(TurtleValue::Iri(Iri::Prefixed {
prefix: "",
local_name: "a",
},)),
object: Box::new(TurtleValue::Iri(Iri::Prefixed {
prefix: "",
local_name: "b",
},)),
},]
.into(),
}
);
}
#[test]
fn labeled_bnode_using_proper_rules_test2() {
let s = "_:b-node :a :b";
let (rest, res) = triples(s).unwrap();
assert!(rest.trim().is_empty());
assert_eq!(
res,
TurtleValue::Statement {
subject: Box::new(TurtleValue::BNode(BlankNode::Labeled("b-node",),)),
predicate_objects: [TurtleValue::PredicateObject {
predicate: Box::new(TurtleValue::Iri(Iri::Prefixed {
prefix: "",
local_name: "a",
},)),
object: Box::new(TurtleValue::Iri(Iri::Prefixed {
prefix: "",
local_name: "b",
},)),
},]
.into(),
}
);
}
#[test]
fn labeled_bnode_using_proper_rules_test3() {
let s = "_:b·node :a :b";
let (rest, res) = triples(s).unwrap();
assert!(rest.trim().is_empty());
assert_eq!(
res,
TurtleValue::Statement {
subject: Box::new(TurtleValue::BNode(BlankNode::Labeled("b·node",),)),
predicate_objects: [TurtleValue::PredicateObject {
predicate: Box::new(TurtleValue::Iri(Iri::Prefixed {
prefix: "",
local_name: "a",
},)),
object: Box::new(TurtleValue::Iri(Iri::Prefixed {
prefix: "",
local_name: "b",
},)),
},]
.into(),
}
);
}

#[test]
fn labeled_bnode_using_proper_rules_test4() {
let s = "_:b-jöhn :a :b";
let (rest, res) = triples(s).unwrap();
assert!(rest.trim().is_empty());
assert_eq!(
res,
TurtleValue::Statement {
subject: Box::new(TurtleValue::BNode(BlankNode::Labeled("b-jöhn",),)),
predicate_objects: [TurtleValue::PredicateObject {
predicate: Box::new(TurtleValue::Iri(Iri::Prefixed {
prefix: "",
local_name: "a",
},)),
object: Box::new(TurtleValue::Iri(Iri::Prefixed {
prefix: "",
local_name: "b",
},)),
},]
.into(),
}
);
}

#[test]
fn labeled_bnode_using_proper_rules_test5() {
let s = "_:b_undertie‿node :a :b";
let (rest, res) = triples(s).unwrap();
assert!(rest.trim().is_empty());
assert_eq!(
res,
TurtleValue::Statement {
subject: Box::new(TurtleValue::BNode(BlankNode::Labeled("b_undertie‿node",),)),
predicate_objects: [TurtleValue::PredicateObject {
predicate: Box::new(TurtleValue::Iri(Iri::Prefixed {
prefix: "",
local_name: "a",
},)),
object: Box::new(TurtleValue::Iri(Iri::Prefixed {
prefix: "",
local_name: "b",
},)),
},]
.into(),
}
);
}
#[test]
fn collection_test() {
let s = r#":a :b ( "apple" "banana" ) ."#;
Expand Down

0 comments on commit 18ff897

Please sign in to comment.