use crate::shapemap::{NodeSelector, Pattern, SHACLPathRef, ShapeSelector};
use crate::{
IRes, ParseError, Span,
compact::grammar::{map_error, tag_no_case_tws, token_tws, traced, tws0},
compact::shex_grammar::shape_expr_label,
iri, literal,
};
use crate::{ObjectValue, string};
use nom::bytes::complete::tag;
use nom::{
Parser,
branch::alt,
character::complete::char,
combinator::{all_consuming, map, opt},
multi::many0,
};
use prefixmap::IriRef;
use rudof_rdf::rdf_core::vocabs::RdfVocab;
#[derive(Debug, PartialEq)]
pub(crate) enum ShapeMapStatement {
Association {
node_selector: NodeSelector,
shape_selector: ShapeSelector,
},
}
pub(crate) fn shapemap_statement<'a>() -> impl FnMut(Span<'a>) -> IRes<'a, Vec<ShapeMapStatement>> {
traced(
"shapemap_statement",
map_error(
move |i| {
let (i, (a, _, ass, _, _, _)) =
all_consuming((association, tws0, rest_associations, tws0, opt(char(',')), tws0)).parse(i)?;
let mut rs = vec![a];
for a in ass {
rs.push(a);
}
Ok((i, rs))
},
|| ParseError::ExpectedShapeMapAssociation,
),
)
}
fn association(i: Span) -> IRes<ShapeMapStatement> {
let (i, (ns, _, sl)) = (node_selector(), token_tws("@"), shape_spec()).parse(i)?;
let s = ShapeMapStatement::Association {
node_selector: ns,
shape_selector: sl,
};
Ok((i, s))
}
fn rest_associations(i: Span) -> IRes<Vec<ShapeMapStatement>> {
let (i, ass) = many0((token_tws(","), tws0, association)).parse(i)?;
let r = ass.into_iter().map(|(_, _, a)| a).collect();
Ok((i, r))
}
pub(crate) fn shape_spec<'a>() -> impl FnMut(Span<'a>) -> IRes<'a, ShapeSelector> {
traced(
"shape_spec",
map_error(
move |i| {
alt((
map(shape_expr_label, ShapeSelector::Label),
map(tag_no_case_tws("START"), |_| ShapeSelector::Start),
))
.parse(i)
},
|| ParseError::ExpectedShapeSpec,
),
)
}
pub(crate) fn node_selector<'a>() -> impl FnMut(Span<'a>) -> IRes<'a, NodeSelector> {
traced(
"node_selector",
map_error(
move |i| alt((object_term, triple_pattern, extended)).parse(i),
|| ParseError::ExpectedNodeSpec,
),
)
}
fn object_term(i: Span) -> IRes<NodeSelector> {
alt((subject_term, literal_selector)).parse(i)
}
fn object_value<'a>() -> impl FnMut(Span<'a>) -> IRes<'a, ObjectValue> {
move |i| alt((map(iri, ObjectValue::iri_ref), map(literal(), ObjectValue::literal))).parse(i)
}
fn triple_pattern(i: Span) -> IRes<NodeSelector> {
let (i, (_, _, triple, _, _)) = (open_curly, tws0, triple_pattern_inner, tws0, close_curly).parse(i)?;
Ok((i, triple))
}
fn triple_pattern_inner(i: Span) -> IRes<NodeSelector> {
alt((focus_object, subject_focus)).parse(i)
}
fn node_or_wildcard(i: Span) -> IRes<Pattern> {
alt((
map(object_value(), Pattern::Node),
map(token_tws("_"), |_| Pattern::wildcard()),
))
.parse(i)
}
fn focus_object(i: Span) -> IRes<NodeSelector> {
let (i, (_, _, path, _, pattern)) = (focus, tws0, shacl_path, tws0, node_or_wildcard).parse(i)?;
Ok((
i,
NodeSelector::TriplePattern {
subject: Pattern::Focus,
path,
object: pattern,
},
))
}
fn subject_focus(i: Span) -> IRes<NodeSelector> {
let (i, (pattern, _, path, _, _)) = (node_or_wildcard, tws0, shacl_path, tws0, focus).parse(i)?;
Ok((
i,
NodeSelector::TriplePattern {
subject: pattern,
path,
object: Pattern::Focus,
},
))
}
fn shacl_path(i: Span) -> IRes<SHACLPathRef> {
map(predicate, SHACLPathRef::predicate).parse(i)
}
fn predicate(i: Span) -> IRes<IriRef> {
alt((iri, rdf_type)).parse(i)
}
fn rdf_type(i: Span) -> IRes<IriRef> {
let (i, _) = tag("a")(i)?;
let rdf_type: IriRef = IriRef::iri(RdfVocab::rdf_type());
Ok((i, rdf_type))
}
fn focus(i: Span) -> IRes<Pattern> {
let (i, _) = tag_no_case_tws("FOCUS")(i)?;
Ok((i, Pattern::Focus))
}
fn extended(i: Span) -> IRes<NodeSelector> {
let (i, (_keyword, _, query)) = (tag_no_case_tws("SPARQL"), tws0, string()).parse(i)?;
Ok((i, NodeSelector::Sparql { query }))
}
fn subject_term(i: Span) -> IRes<NodeSelector> {
let (i, iri) = iri(i)?;
Ok((i, NodeSelector::iri_ref(iri)))
}
fn literal_selector(i: Span) -> IRes<NodeSelector> {
let (i, lit) = literal()(i)?;
Ok((i, NodeSelector::literal(lit)))
}
fn open_curly(i: Span) -> IRes<char> {
char('{')(i)
}
fn close_curly(i: Span) -> IRes<char> {
char('}')(i)
}
#[cfg(test)]
mod tests {
use prefixmap::IriRef;
use crate::shapemap::ShapeSelector;
use super::*;
#[test]
fn example_shapemap() {
let input = Span::new(":a@:label");
let (_, shape_map) = association(input).unwrap();
let expected = ShapeMapStatement::Association {
node_selector: NodeSelector::prefixed("", "a"),
shape_selector: ShapeSelector::prefixed("", "label"),
};
assert_eq!(shape_map, expected);
}
#[test]
fn example_shapemap_sparql() {
let query = r#""""foo""""#;
let str = format!("SPARQL {query}@:label");
println!("Str: {str}");
let input = Span::new(str.as_str());
let (_, shape_map) = association(input).unwrap();
let expected = ShapeMapStatement::Association {
node_selector: NodeSelector::Sparql {
query: "foo".to_string(),
},
shape_selector: ShapeSelector::prefixed("", "label"),
};
assert_eq!(shape_map, expected);
}
#[test]
fn shapemap_triple_pattern() {
let input = Span::new("{ FOCUS a :Person }@:label");
let (_, shape_map) = association(input).unwrap();
let expected = ShapeMapStatement::Association {
node_selector: NodeSelector::triple_pattern(
Pattern::focus(),
SHACLPathRef::predicate(IriRef::iri(RdfVocab::rdf_type())),
Pattern::prefixed("", "Person"),
),
shape_selector: ShapeSelector::prefixed("", "label"),
};
assert_eq!(shape_map, expected);
}
#[test]
fn test_triple_pattern() {
let input = Span::new("{ FOCUS a :Person }");
let (_, tp) = triple_pattern(input).unwrap();
let expected = NodeSelector::triple_pattern(
Pattern::focus(),
SHACLPathRef::predicate(IriRef::iri(RdfVocab::rdf_type())),
Pattern::prefixed("", "Person"),
);
assert_eq!(tp, expected);
}
#[test]
fn test_triple_pattern_inner() {
let input = Span::new("FOCUS a :Person");
let (_, value) = triple_pattern_inner(input).unwrap();
let expected = NodeSelector::triple_pattern(
Pattern::focus(),
SHACLPathRef::predicate(IriRef::iri(RdfVocab::rdf_type())),
Pattern::prefixed("", "Person"),
);
assert_eq!(value, expected);
}
#[test]
fn test_focus_object() {
let input = Span::new("FOCUS a :Person");
let (_, value) = focus_object(input).unwrap();
let expected = NodeSelector::triple_pattern(
Pattern::focus(),
SHACLPathRef::predicate(IriRef::iri(RdfVocab::rdf_type())),
Pattern::prefixed("", "Person"),
);
assert_eq!(value, expected);
}
#[test]
fn test_focus() {
let input = Span::new("FOCUS");
let (_, value) = focus(input).unwrap();
let expected = Pattern::focus();
assert_eq!(value, expected);
}
#[test]
fn test_shapemap_statement_no_trailing_comma() {
let input = Span::new(":a@:A");
let (_, stmts) = shapemap_statement()(input).unwrap();
assert_eq!(stmts.len(), 1);
}
#[test]
fn test_shapemap_statement_with_trailing_comma() {
let input = Span::new(":a@:A,");
let (_, stmts) = shapemap_statement()(input).unwrap();
assert_eq!(stmts.len(), 1);
}
#[test]
fn test_shapemap_statement_multiple_no_trailing_comma() {
let input = Span::new(":a@:A, :b@:B");
let (_, stmts) = shapemap_statement()(input).unwrap();
assert_eq!(stmts.len(), 2);
}
#[test]
fn test_shapemap_statement_multiple_with_trailing_comma() {
let input = Span::new(":a@:A, :b@:B,");
let (_, stmts) = shapemap_statement()(input).unwrap();
assert_eq!(stmts.len(), 2);
}
#[test]
fn test_shapemap_statement_multiple_with_trailing_comma_and_space() {
let input = Span::new(":a@:A, :b@:B, ");
let (_, stmts) = shapemap_statement()(input).unwrap();
assert_eq!(stmts.len(), 2);
}
}