#![cfg_attr(feature = "cargo-clippy", allow(redundant_closure_call))]
use super::grammar_helper::*;
use super::grammar_structs::*;
use constants;
use nom::types::CompleteStr;
use nom::{Err, ErrorKind, IResult, Needed};
macro_rules! one_if (
($i:expr, $f:expr) => (
{
if let Some(c) = $i.chars().next() {
if $f(c) {
Ok((CompleteStr(&$i[1..]), CompleteStr(&$i[..1])))
} else {
Err(::nom::Err::Error(error_position!($i, ErrorKind::OneOf)))
}
} else {
Err(::nom::Err::Incomplete::<_, _>(Needed::Size(1)))
}
}
);
);
fn not_eol(c: char) -> bool {
c != '\n' && c != '\r'
}
named!(comment<CompleteStr,CompleteStr>, do_parse!(
char!('#') >>
comment: take_while!(not_eol) >>
alt!(tag!("\n") | tag!("\r") | eof!()) >>
(comment)
));
named!(pub tws<CompleteStr, ()>, fold_many0!(
alt!(map!(one_of!(" \t\n\r"),|_|())
| map!(comment,|_|())
), (), |_,_|()
));
named!(pub statement<CompleteStr,Statement>, alt!(statement_triples
| prefix_id | base | sparql_prefix | sparql_base));
named!(statement_triples<CompleteStr,Statement>, do_parse!(
triples: triples >> tws >>
char!('.') >>
(Statement::Triples(triples))
));
named!(prefix_id<CompleteStr,Statement>, do_parse!(
tag!("@prefix") >> tws >>
pname_ns: pname_ns >> tws >>
iri_ref: iri_ref >> tws >>
char!('.') >>
(Statement::Prefix(pname_ns.0, iri_ref.0))
));
named!(base<CompleteStr,Statement>, do_parse!(
tag!("@base") >> tws >>
iri_ref: iri_ref >> tws >>
char!('.') >>
(Statement::Base(iri_ref.0))
));
named!(sparql_base<CompleteStr,Statement>, do_parse!(
tag_no_case!("BASE") >> tws >>
iri_ref: iri_ref >>
(Statement::Base(iri_ref.0))
));
named!(sparql_prefix<CompleteStr,Statement>, do_parse!(
tag_no_case!("PREFIX") >> tws >>
pname_ns: pname_ns >> tws >>
iri_ref: iri_ref >>
(Statement::Prefix(pname_ns.0, iri_ref.0))
));
named!(triples<CompleteStr,Triples>, alt!(triples_subject | triples_blank));
named!(triples_subject<CompleteStr,Triples>, do_parse!(
subject: subject >> tws >>
predicated_objects_list: predicated_objects_list >>
(Triples{
subject,
predicated_objects_list
})
));
fn triples_blank(str: CompleteStr) -> IResult<CompleteStr, Triples> {
match blank_node_property_list(str) {
Ok((mut left, mut blank)) => {
if let Ok((l, _)) = tws(left) {
match predicated_objects_list(l) {
Ok((l, mut pol)) => {
left = l;
blank.append(&mut pol);
}
Err(Err::Incomplete(i)) => return Err(Err::Incomplete(i)),
_ => {}
}
}
let t = Triples {
subject: Subject::BlankNode(BlankNode::Anon),
predicated_objects_list: blank,
};
Ok((left, t))
}
Err(e) => Err(e),
}
}
fn predicated_objects_list(mut str: CompleteStr) -> IResult<CompleteStr, Vec<PredicatedObjects>> {
let mut v = Vec::new();
if let Ok((left, _)) = tws(str) {
str = left;
}
match predicated_object(str) {
Ok((left, po)) => {
v.push(po);
str = left;
}
Err(e) => return Err(e),
}
loop {
match predicated_object_sep(str) {
Ok((left, _)) => {
str = left;
}
_ => return Ok((str, v)),
}
match predicated_object(str) {
Ok((left, po)) => {
v.push(po);
str = left;
}
_ => return Ok((str, v)),
}
}
}
named!(predicated_object_sep<CompleteStr,()>,
fold_many1!(tuple!(tws, char!(';'), tws),(),|_,_|())
);
named!(predicated_object<CompleteStr,PredicatedObjects>, do_parse!(
verb: verb >>
tws >>
objects: object_list >>
(PredicatedObjects{
verb,
objects
})
));
named!(object_list<CompleteStr,Vec<Object> >, separated_nonempty_list!(
tuple!(tws, char!(','), tws),
object
));
named!(verb<CompleteStr,IRI>, alt!(iri|a));
named!(a<CompleteStr,IRI>, value!(
IRI::IRI(constants::RDF_TYPE),
char!('a')
));
named!(subject<CompleteStr,Subject>, alt!(
map!(iri, Subject::IRI) |
map!(blank_node, Subject::BlankNode) |
map!(collection, Subject::Collection)
));
named!(object<CompleteStr,Object>, alt!(
map!(literal, Object::Literal) |
map!(iri, Object::IRI) |
map!(blank_node, Object::BlankNode) |
map!(collection, Object::Collection) |
map!(blank_node_property_list, Object::BlankNodePropertyList)
));
named!(literal<CompleteStr,Literal>, alt!(rdfliteral | boolean | double | decimal | integer));
named!(blank_node_property_list<CompleteStr,Vec<PredicatedObjects> >, do_parse!(
char!('[') >> tws >>
pol: predicated_objects_list >> tws >>
char!(']') >> (pol)
));
named!(collection<CompleteStr,Vec<Object> >, do_parse!(
char!('(') >> tws >>
objects: many0!(do_parse!(
object: object >> tws >>
(object))) >>
char!(')') >> (objects)
));
named!(rdfliteral<CompleteStr,Literal>, do_parse!(
string: string >>
datatype: opt!(alt!(langtag | iri_ref_literal)) >>
({
match datatype {
Some(RDFLiteralType::LangTag(langtag)) => {
Literal {
lexical: string,
datatype: Datatype::RDFLangString,
language: Some(langtag)
}
},
Some(RDFLiteralType::DataType(datatype)) => {
Literal {
lexical: string,
datatype: Datatype::IRI(datatype),
language: None
}
},
None => {
Literal {
lexical: string,
datatype: Datatype::XSDString,
language: None
}
}
}
})
));
named!(iri_ref_literal<CompleteStr,RDFLiteralType>, do_parse!(
tag!("^^") >>
iri: iri >>
(RDFLiteralType::DataType(iri))
));
named!(pub boolean<CompleteStr,Literal>, do_parse!(
b: alt!(tag!("true") | tag!("false")) >>
(Literal {
lexical: b.0,
datatype: Datatype::XSDBoolean,
language: None
})
));
named!(string<CompleteStr,&str>, alt!(string_literal_long_single_quote
| string_literal_long_quote | string_literal_quote
| string_literal_single_quote));
named!(iri<CompleteStr,IRI>, alt!(iri_iri|prefixed_name));
named!(prefixed_name<CompleteStr,IRI>, do_parse!(
pn_prefix: opt!(pn_prefix) >>
char!(':') >>
pn_local: opt!(pn_local) >>
(IRI::PrefixedName(
pn_prefix.map(|p|p.0).unwrap_or(""),
pn_local.map(|p|p.0).unwrap_or("")
))
));
named!(blank_node<CompleteStr,BlankNode>, alt!(blank_node_label | anon));
named!(iri_ref<CompleteStr,CompleteStr>, delimited!(
char!('<'),take_while!(is_iri_ref),char!('>')
));
named!(pname_ns<CompleteStr,CompleteStr>, do_parse!(
pn_prefix: opt!(pn_prefix) >>
char!(':') >>
(pn_prefix.unwrap_or(CompleteStr("")))
));
named!(blank_node_label<CompleteStr,BlankNode>, do_parse!(
tag!("_:") >>
label: recognize!(tuple!(
one_if!(is_pn_chars_u_digit),
blank_node_label2
)) >> (BlankNode::BlankNode(label.0))
));
fn is_pn_chars_u_digit(c: char) -> bool {
is_digit(c) || is_pn_chars_u(c)
}
fn is_pn_chars_or_dot(c: char) -> bool {
c == '.' || is_pn_chars(c)
}
fn blank_node_label2(src: CompleteStr) -> IResult<CompleteStr, ()> {
match blank_node_label3(src) {
Ok((left, m)) => {
if m.ends_with('.') {
Ok((CompleteStr(&src[m.len() - 1..]), ()))
} else {
Ok((left, ()))
}
}
Err(e) => Err(e),
}
}
named!(blank_node_label3<CompleteStr,CompleteStr>, take_while!(is_pn_chars_or_dot));
named!(langtag<CompleteStr,RDFLiteralType>, do_parse!(
char!('@') >>
langtag: recognize!(tuple!(
alpha,
opt!(tuple!(char!('-'), alphanumeric))
)) >>
(RDFLiteralType::LangTag(langtag.0))
));
named!(pub integer<CompleteStr,Literal>, map!(recognize!(tuple!(
opt!(one_of!("+-")), digit)),
(|integer|{
Literal {
lexical: integer.0,
datatype: Datatype::XSDInteger,
language: None
}
})
));
named!(pub decimal<CompleteStr,Literal>, map!(recognize!(tuple!(
opt!(one_of!("+-")), opt_digit, char!('.'), digit)),
(|decimal|{
Literal {
lexical: decimal.0,
datatype: Datatype::XSDDecimal,
language: None
}
})
));
named!(pub double<CompleteStr,Literal>, map!(recognize!(tuple!(
opt!(one_of!("+-")),
alt!(
recognize!(tuple!(digit,char!('.'), opt_digit, exponent)) |
recognize!(tuple!(opt!(char!('.')), digit, exponent))
))),
(|double|{
Literal {
lexical: double.0,
datatype: Datatype::XSDDouble,
language: None
}
})
));
named!(exponent<CompleteStr,()>, map!(tuple!(
one_of!("Ee"),opt!(one_of!("+-")), digit),
(|_|())
));
fn string_literal_quote(str: CompleteStr) -> IResult<CompleteStr, &str> {
string_literal(str, 1, start_quote, find_quote)
}
fn start_quote(s: CompleteStr) -> bool {
s.starts_with('"')
}
fn find_quote(s: CompleteStr) -> Option<usize> {
s.find('"')
}
fn string_literal_single_quote(str: CompleteStr) -> IResult<CompleteStr, &str> {
string_literal(str, 1, start_single_quote, find_single_quote)
}
fn start_single_quote(s: CompleteStr) -> bool {
s.starts_with('\'')
}
fn find_single_quote(s: CompleteStr) -> Option<usize> {
s.find('\'')
}
fn string_literal_long_single_quote(str: CompleteStr) -> IResult<CompleteStr, &str> {
string_literal(str, 3, start_long_single_quote, find_long_single_quote)
}
fn start_long_single_quote(s: CompleteStr) -> bool {
s.starts_with("'''")
}
fn find_long_single_quote(s: CompleteStr) -> Option<usize> {
s.find("'''")
}
fn string_literal_long_quote(str: CompleteStr) -> IResult<CompleteStr, &str> {
string_literal(str, 3, start_long_quote, find_long_quote)
}
fn start_long_quote(s: CompleteStr) -> bool {
s.starts_with("\"\"\"")
}
fn find_long_quote(s: CompleteStr) -> Option<usize> {
s.find("\"\"\"")
}
named!(anon<CompleteStr,BlankNode>, do_parse!(
char!('[') >>
tws >>
char!(']') >> (BlankNode::Anon)
));
fn is_pn_chars_base(c: char) -> bool {
is_alpha(c) || in_range(c, 0xC0, 0x00D6) || in_range(c, 0x00D8, 0x00F6)
|| in_range(c, 0x00F8, 0x02FF) || in_range(c, 0x0370, 0x037D)
|| in_range(c, 0x037F, 0x1FFF) || in_range(c, 0x200C, 0x200D)
|| in_range(c, 0x2070, 0x218F) || in_range(c, 0x2C00, 0x2FEF)
|| in_range(c, 0x3001, 0xD7FF) || in_range(c, 0xF900, 0xFDCF)
|| in_range(c, 0xFDF0, 0xFFFD) || in_range(c, 0x10000, 0xEFFFF)
}
fn is_pn_chars_u(c: char) -> bool {
c == '_' || is_pn_chars_base(c)
}
fn is_pn_chars(c: char) -> bool {
is_pn_chars_u(c) || c == '-' || is_digit(c) || c == 0xB7 as char || in_range(c, 0x0300, 0x036F)
|| in_range(c, 0x203F, 0x2040)
}
named!(pn_prefix<CompleteStr,CompleteStr>, recognize!(tuple!(
one_if!(is_pn_chars_base),
take_while!(is_pn_chars),
fold_many0!(tuple!(
char!('.'),
take_while1!(is_pn_chars)
),(),|_,_|())
)));
named!(pub pn_local<CompleteStr,CompleteStr>, recognize!(tuple!(
alt!(one_if!(is_pn_local_start) | plx),
pn_local2
)));
fn pn_local2(src: CompleteStr) -> IResult<CompleteStr, ()> {
match pn_local3(src) {
Ok((left, m)) => {
if m.ends_with('.') {
Ok((CompleteStr(&src[m.len() - 1..]), ()))
} else {
Ok((left, ()))
}
}
Err(e) => Err(e),
}
}
named!(pn_local3<CompleteStr,CompleteStr>,
recognize!(many0!(alt!(pn_chars_colon | plx | tag!(".")))));
named!(pn_chars_colon<CompleteStr,CompleteStr>, take_while1!(is_pn_chars_colon));
fn is_pn_local_start(c: char) -> bool {
c == ':' || is_digit(c) || is_pn_chars_u(c)
}
fn is_pn_chars_colon(c: char) -> bool {
c == ':' || is_pn_chars(c)
}
named!(plx<CompleteStr,CompleteStr>, alt!(percent | pn_local_esc));
named!(percent<CompleteStr,CompleteStr>, recognize!(tuple!(
char!('%'),
one_if!(is_hex),
one_if!(is_hex)
)));
named!(pn_local_esc<CompleteStr,CompleteStr>, recognize!(tuple!(
char!('\\'),
one_if!(|c| "_~.-!$&'()*+,;=/?#@%".contains(c))
)));
fn is_alpha(c: char) -> bool {
(c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z')
}
fn is_alphanum(c: char) -> bool {
is_alpha(c) || is_digit(c)
}
fn is_digit(c: char) -> bool {
c >= '0' && c <= '9'
}
fn is_hex(c: char) -> bool {
is_digit(c) || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F')
}
fn in_range(c: char, lower: u32, upper: u32) -> bool {
c as u32 >= lower && c as u32 <= upper
}
#[test]
fn test_comment() {
use nom::Context;
assert_eq!(
comment(CompleteStr("#\r\na")),
Ok((CompleteStr("\na"), CompleteStr("")))
);
assert_eq!(
comment(CompleteStr("#\n\ra")),
Ok((CompleteStr("\ra"), CompleteStr("")))
);
assert_eq!(
comment(CompleteStr("")),
Err(Err::Error(Context::Code(CompleteStr(""), ErrorKind::Eof)))
);
assert_eq!(
comment(CompleteStr("#")),
Ok((CompleteStr(""), CompleteStr("")))
);
assert_eq!(
comment(CompleteStr("#abc")),
Ok((CompleteStr(""), CompleteStr("abc")))
);
assert_eq!(
comment(CompleteStr("#\n\n")),
Ok((CompleteStr("\n"), CompleteStr("")))
);
}
#[test]
fn test_prefixed_name() {
assert_eq!(
prefixed_name(CompleteStr("a:a ")),
Ok((CompleteStr(" "), IRI::PrefixedName("a", "a")))
);
assert_eq!(
prefixed_name(CompleteStr(": ")),
Ok((CompleteStr(" "), IRI::PrefixedName("", "")))
);
}
named!(alpha<CompleteStr,CompleteStr>, take_while1!(is_alpha));
named!(alphanumeric<CompleteStr,CompleteStr>, take_while1!(is_alphanum));
named!(digit<CompleteStr,CompleteStr>, take_while1!(is_digit));
named!(opt_digit<CompleteStr,CompleteStr>, take_while!(is_digit));
#[inline]
fn is_iri_ref(chr: char) -> bool {
chr > ' ' && "<>\"{}|^`".find(chr) == None
}
named!(iri_iri<CompleteStr,IRI>, map!(iri_ref, |v| IRI::IRI(v.0)));
#[test]
fn test_iri() {
assert_eq!(
iri(CompleteStr("<urn:123>")),
Ok((CompleteStr(""), IRI::IRI("urn:123")))
);
}
#[test]
fn test_string_literal_quote() {
assert_eq!(
string_literal_quote(CompleteStr("\"\\\\\"")),
Ok((CompleteStr(""), "\\\\"))
);
}
#[test]
fn test_string_literal_single_quote() {
assert_eq!(
string_literal_single_quote(CompleteStr("''")),
Ok((CompleteStr(""), ""))
);
}
#[test]
fn test_string_literal_long_single_quote() {
assert_eq!(
string_literal_long_single_quote(CompleteStr("''''''")),
Ok((CompleteStr(""), ""))
);
}
#[test]
fn test_string_literal_long_quote() {
assert_eq!(
string_literal_long_quote(CompleteStr("\"\"\"\\U0001f435\"\"\"")),
Ok((CompleteStr(""), "\\U0001f435"))
);
assert_eq!(
string_literal_long_quote(CompleteStr("\"\"\"first long literal\"\"\"")),
Ok((CompleteStr(""), "first long literal"))
);
}
#[test]
fn test_langtag() {
assert_eq!(
langtag(CompleteStr("@nl ")),
Ok((CompleteStr(" "), RDFLiteralType::LangTag("nl")))
);
assert_eq!(
langtag(CompleteStr("@nl-NL ")),
Ok((CompleteStr(" "), RDFLiteralType::LangTag("nl-NL")))
);
}
#[test]
fn test_rdfliteral() {
let r = Literal {
lexical: "",
datatype: Datatype::XSDString,
language: None,
};
assert_eq!(rdfliteral(CompleteStr("'' ")), Ok((CompleteStr(" "), r)));
}
#[cfg(test)]
fn literal_true<'a>() -> Literal<'a> {
Literal {
lexical: "true",
datatype: Datatype::XSDBoolean,
language: None,
}
}
#[cfg(test)]
fn literal_false<'a>() -> Literal<'a> {
Literal {
lexical: "false",
datatype: Datatype::XSDBoolean,
language: None,
}
}
#[cfg(test)]
fn literal_11<'a>() -> Literal<'a> {
Literal {
lexical: "11",
datatype: Datatype::XSDInteger,
language: None,
}
}
#[cfg(test)]
fn literal_d11<'a>() -> Literal<'a> {
Literal {
lexical: "11.1",
datatype: Datatype::XSDDecimal,
language: None,
}
}
#[test]
fn test_integer() {
assert_eq!(
literal(CompleteStr("11 ")),
Ok((CompleteStr(" "), literal_11()))
);
assert_eq!(
literal(CompleteStr("+1 ")),
Ok((
CompleteStr(" "),
Literal {
lexical: "+1",
datatype: Datatype::XSDInteger,
language: None
}
))
);
assert_eq!(
integer(CompleteStr("-1 ")),
Ok((
CompleteStr(" "),
Literal {
lexical: "-1",
datatype: Datatype::XSDInteger,
language: None
}
))
);
}
#[test]
fn test_decimal() {
assert_eq!(
literal(CompleteStr("11.1 ")),
Ok((CompleteStr(" "), literal_d11()))
);
assert_eq!(
literal(CompleteStr("+1.1 ")),
Ok((
CompleteStr(" "),
Literal {
lexical: "+1.1",
datatype: Datatype::XSDDecimal,
language: None
}
))
);
assert_eq!(
literal(CompleteStr("-1.1 ")),
Ok((
CompleteStr(" "),
Literal {
lexical: "-1.1",
datatype: Datatype::XSDDecimal,
language: None
}
))
);
assert_eq!(
literal(CompleteStr(".1 ")),
Ok((
CompleteStr(" "),
Literal {
lexical: ".1",
datatype: Datatype::XSDDecimal,
language: None
}
))
);
}
#[test]
fn test_boolean() {
assert_eq!(
boolean(CompleteStr("true")),
Ok((CompleteStr(""), literal_true()))
);
assert_eq!(
boolean(CompleteStr("false")),
Ok((CompleteStr(""), literal_false()))
);
}
#[test]
fn test_literal() {
assert_eq!(
literal(CompleteStr("true")),
Ok((CompleteStr(""), literal_true()))
);
assert_eq!(
literal(CompleteStr("false")),
Ok((CompleteStr(""), literal_false()))
);
}
#[test]
fn test_object() {
assert_eq!(
object(CompleteStr("_:b1 ")),
Ok((
CompleteStr(" "),
Object::BlankNode(BlankNode::BlankNode("b1"))
))
);
let long = Object::Literal(Literal {
lexical: "first long literal",
datatype: Datatype::XSDString,
language: None,
});
assert_eq!(
object(CompleteStr("\"\"\"first long literal\"\"\" ")),
Ok((CompleteStr(" "), long))
);
}
#[test]
fn test_blank_node_label() {
assert_eq!(
blank_node_label(CompleteStr("_:b1 ")),
Ok((CompleteStr(" "), BlankNode::BlankNode("b1")))
);
assert_eq!(
blank_node_label(CompleteStr("_:b1. ")),
Ok((CompleteStr(". "), BlankNode::BlankNode("b1")))
);
}
#[test]
fn test_object_list() {
let v = vec![
Object::Literal(literal_true()),
Object::Literal(literal_11()),
Object::Literal(literal_false()),
];
assert_eq!(
object_list(CompleteStr("true, 11 , false ")),
Ok((CompleteStr(" "), v))
);
}
#[test]
fn test_predicated_objects() {
let v = vec![Object::Literal(Literal {
lexical: "1",
datatype: Datatype::XSDInteger,
language: None,
})];
let po = PredicatedObjects {
verb: IRI::IRI("urn:123"),
objects: v,
};
assert_eq!(
predicated_objects_list(CompleteStr("<urn:123> 1 ")),
Ok((CompleteStr(" "), vec![po]))
);
}
#[test]
fn test_triples() {
let v = vec![Object::Literal(Literal {
lexical: "1",
datatype: Datatype::XSDInteger,
language: None,
})];
let i = IRI::IRI("urn:123");
let s = Subject::IRI(i.clone());
let po = vec![PredicatedObjects {
verb: i.clone(),
objects: v,
}];
let t = Triples {
subject: s,
predicated_objects_list: po,
};
assert_eq!(
triples(CompleteStr("<urn:123> <urn:123> 1 ")),
Ok((CompleteStr(" "), t))
);
}
#[test]
fn test_statement_triples() {
let i = IRI::PrefixedName("", "");
let s = Subject::IRI(i.clone());
let po = vec![PredicatedObjects {
verb: i.clone(),
objects: vec![Object::IRI(i.clone())],
}];
let t = Triples {
subject: s,
predicated_objects_list: po,
};
let s = Statement::Triples(t);
assert_eq!(
statement_triples(CompleteStr(": : :.")),
Ok((CompleteStr(""), s))
);
}
#[test]
fn test_prefix_id() {
assert_eq!(
prefix_id(CompleteStr("@prefix a.b.c: <urn> .")),
Ok((CompleteStr(""), Statement::Prefix("a.b.c", "urn")))
);
assert_eq!(
prefix_id(CompleteStr("@prefix : <urn> .")),
Ok((CompleteStr(""), Statement::Prefix("", "urn")))
);
}
#[test]
fn test_base() {
assert_eq!(
base(CompleteStr("@base <urn> .")),
Ok((CompleteStr(""), Statement::Base("urn")))
);
}
#[test]
fn test_sparql_base() {
assert_eq!(
sparql_base(CompleteStr("BASE <urn>")),
Ok((CompleteStr(""), Statement::Base("urn")))
);
}
#[test]
fn test_sparql_prefix() {
assert_eq!(
sparql_prefix(CompleteStr("PREFIX a.b.c: <urn>")),
Ok((CompleteStr(""), Statement::Prefix("a.b.c", "urn")))
);
}
#[test]
fn test_pn_local() {
assert_eq!(
pn_local(CompleteStr("c. ")),
Ok((CompleteStr(". "), CompleteStr("c")))
);
}