LeanRDF

Reading and writing RDF

LeanRDF reads N-Triples and Turtle natively and writes canonical N-Triples. Both parsers are hand-written recursive descent, build terms only through the smart constructors of chapter 2, and report the line and column of every error.

N-Triples

N-Triples is one triple per line, absolute IRIs only:

#eval NTriples.parse "<http://example/s> <http://example/p> \"Cheers\"@en-UK ."
-- a graph whose object is Literal.lang "Cheers" "en-uk"

The serializer writes canonical N-Triples (RDF 1.1 N-Triples §4): sorted, one line per triple, no \u escapes, and ^^xsd:string omitted. It refuses, with a reason, anything that would not parse back: a literal subject, a relative IRI, an invalid blank-node label.

#guard (Graph.ofList [⟨ex "s", ex "p", .lit (Literal.str "x")⟩]).toNTriples.toOption
  == some "<http://example/s> <http://example/p> \"x\" .\n"

Round trips are stable: on every positive W3C test, parse, serialize, parse, serialize gives equal graphs and identical text.

Turtle

Turtle is what people write: prefixes, a base IRI, ; and , to share subjects and predicates, a for rdf:type, [ … ] for anonymous nodes, ( … ) for lists, and shorthand numbers and booleans.

#eval Turtle.parse "
  @prefix ex: <http://example.org/> .
  ex:s a ex:C ; ex:p 42, -1.5, 1E3, true, ( ex:a ex:b ) ."

A few details matter for meaning:

Datasets: N-Quads and TriG

SPARQL and named graphs need more than one graph. An RDF dataset is a default graph and any number of named graphs, each named by an IRI or a blank node. In LeanRDF a Dataset is a set of Quads, a triple with g : Option Term (none is the default graph), and like Graph it is used only through membership lemmas such as mem_namedGraph : t ∈ d.namedGraph n ↔ Quad.ofTriple t (some n) ∈ d.

N-Quads adds an optional graph label to an N-Triples line; TriG adds graph blocks to Turtle and shares its parser:

#eval TriG.parse "
  @prefix ex: <http://example.org/> .
  ex:a ex:p ex:b .                       # default graph
  GRAPH ex:g { ex:c ex:p ex:d }          # named graph ex:g
  _:h { ex:e ex:p ex:f }" "http://example.org/"

RDF 1.1 gives datasets no model theory, so entailment stays a relation between graphs.

Conformance

Both parsers are tested against the W3C suites, vendored in Tests/data/ and run on every lake test:

SuitePositiveNegativeEvaluation
RDF 1.1 N-Triples41 / 4129 / 29(round trips)
RDF 1.1 Turtle74 / 7494 / 94145 / 145
RDF 1.1 N-Quads53 / 5334 / 34
RDF 1.1 TriG99 / 99115 / 115143 / 143

Evaluation tests compare the parsed graph (or dataset) with the expected N-Triples (or N-Quads) up to blank-node renaming.

One deliberate choice: the 2014 grammar allows : inside blank-node labels, but the current W3C suite rejects _:abc:def. LeanRDF follows the tests and records the choice in its design notes.