n3s - A Notation3 Proprocessor

n3s is a preprocessor and command line editor (i.e. good replacement for echo!) for the Notation3 serialization of RDF. It also serves as a good pretty-printer, getting rid of quirky whitespace etc.

Features

Getting The Program

You can either get the source (preferable) which requires Python to run, or get a precompiled Windows binary.

WinZip should be able to read .tar.gz files, although you may have to rename the file n3s.zip in order to get Win to recognize it.

Using The Program

To access the builtin documentation, run "python n3s.py".

 Basic usage: python n3s -[pus]

   -p: pipe in
      e.g. `cat FileName | python n3s -p`
   -u: use file (default)
      e.g. `python n3s -u FileOrURI` or `python n3s FileOrURI`
   -s: use command line string
      e.g. `python n3s -s '{ ?x rdfs:label "x" } => { :Test a :Pass }'`

 Further Usage: python n3s -[td][pus][i[=id]]

   -t tokenize
   -d debug
   -i load a file, and use the stuff declared within

In its basic mode, n3s can take in some abbreviated Notation3 from pipe (STDIN), URI/filename, or command line, and then print out a proper Notation3 variant. For example:-

$ n3s -s ':x :y :z'
@prefix : <#> .

:x :y :z .

Note how the very last period (and only the last period) is not required. For a more useful example:-

$ n3s -s '{ ?x rdfs:label ?z } => { ?z :labelOf ?x }'
@prefix : <#> .
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .

this log:forAll :z , :x . 
{ :x rdfs:label :z } log:implies { :z :labelOf :x } .

Here, ?x and ?z are universally quantified variables, and => stands for log:implies. Note also how n3s guesses the prefixes that are being used; you can change these defaults if you like, or even load them in from another file.

$ n3s http://infomesh.net/2002/apr-test.n3
@prefix : <http://example.org/xyz#> .
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .

this log:forAll :x , :y , :z . 
rdf:type :gunge blargh . 
<http://infomesh.net/test#some> <http://blargh.org/#great> this . 
<http://blargh.org/#wonderful> rdf:type <http://infomesh.net/test#some> . 
<http://infomesh.net/test#some> rdf:type blargh . 
:x :y :z .

Further Usage

AN3: @keyword, @use, and @prefix default

The new @keyword, @use and @prefix default declarations allow one to carve out a space of keywords in a particular Notation3 file. Notation3 already reserves some keywords, such as "a", and "this". It also has a number of non-name component symbols such as "=" and ":-" etc. The new declarations enable one to extend the keywords in a consistent manner, but do not allow you to add new non-name component symbols (which should have been deprecated, IMO).

n3s already knows about the standard Notation3 keywords, and adds a few more: "a", "this", "=", "is", "of", "forall", and "forsome". Note that some of these are syntactic, and some map to a "real" URI. Roughly: "this", "is", "of": don't map; "a": rdf:type; "=": daml:equivalentTo; "forall": log:forAll; "forsome": log:forSome.

The @keyword declarations simply lets the processor know which keywords from the default N3 space you want to reserve. Since some of the keywords in this space are syntactic, keywords in this namespace that n3s does not understand will simply be left "as is".

$ n3s -s '@keyword a, this, blargh . this a blargh .'
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .

this rdf:type blargh .

The @use declaration enables one to import a keyword from a namespace, as it were.

$ n3s -s '@use Sean, likes, TV <http://xyz.org/#> . Sean likes TV'
<http://xyz.org/#Sean> <http://xyz.org/#likes> <http://xyz.org/#TV> .

The @prefix default construct simply lets people say that any keywords that aren't declared in the @keyword or @use spaces are in this space. For example:-

$ n3s -s '@prefix default <http://xyz.org/#> . Sean likes TV'
<http://xyz.org/#Sean> <http://xyz.org/#likes> <http://xyz.org/#TV> .

When you combine the different keyword, here is the order of precedence, in decending order:-

  1. @keyword
  2. @use
  3. implicit @keyword
  4. @prefix default

For example:-

$ n3s -s '@keyword this, blargh . @use this, x, y <http://xyz.org/#> . 
@prefix default <http://blargh.org/#> . this a blargh . x y z .'

@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .

this rdf:type blargh . 
<http://xyz.org/#x> <http://xyz.org/#y> <http://blargh.org/#z> .

If a default is used, and n3s can map it to a QName, it will:-

$ n3s -s '@prefix default <http://xyz.org/#> . @prefix x: 
<http://xyz.org/#> . Sean likes TV'

@prefix x: <http://xyz.org/#> .

x:Sean x:likes x:TV .

From a suggestion by Aaron Swartz, "@prefix default <#> ." is now a built in default setting, thus:-

$ n3s -s 'Sean a Person'
@prefix : <#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .

:Sean rdf:type :Person .

-i

n3s allows you to declare prefixes and keyword in a file, and use those instead of the default. For example, one could save the string '@prefix mypre: <http://blargh.org/#> .' in the file mypre.n3, and then run `n3s -i=mypre.n3 'mypre:x mypre:y mypre:z'`. It can also get files from the Web (use a URI instead of a filename), or from pipe in (leave out the =id bit altogether).

As an example of -i working on real-world data:-

$ n3s -si=http://www.w3.org/2000/10/swap/pim/doc.n3 ':x :y :z . s:label 
r:type r:Property'

@prefix : <http://www.w3.org/2000/10/swap/pim/contacts.n3#> .
@prefix r: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix s: <http://www.w3.org/2000/01/rdf-schema#> .

:x :y :z . 
s:label r:type r:Property .

You can also use a local file by default by editing the "LocalSetup" variable in the source, or by just editing the PREFIXES dictionary.

Other Stuff

Note that when a variable cannot be converted into the default space, n3s will generate an ID for it:-

$ n3s -s '{ ?x ?y :blargh } log:implies { :blargh ?y :x }'
@prefix : <#> .
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
@prefix v: <tag:n3s.infomesh.net,2002-04-13:a917184b23711176b#> .

this log:forAll :y , v:x . 
{ v:x :y :blargh } log:implies { :blargh :y :x } .

Here's a test of the tokenizer:-

$ n3s -s '{ [ :- { :x is ?y of (:b (:p :q)) }; _:b """x""" ] } => { :y ?x 
[ :z (:p) ] }'

@prefix : <#> .
@prefix log: <http://www.w3.org/2000/10/swap/log#> .
@prefix v: <tag:n3s.infomesh.net,2002-04-13:194803ldje2ez5re#> .

this log:forAll v:y , v:x . 
{ [ :- { :x is v:y of (:b (:p :q)) }; 
   _:b "x" ] } log:implies { :y v:x [ :z (:p) ] } .

On the examples on this page, AN3 is roughly 3-5 times shorter than N3.

Limitations and TODO Items

Acknowledgements

Thanks to TimBL for N3, deltab for reminding me that one can quote input on the command line using '', and to myself for coming up with a half decent idea for a change. Thanks also to Aaron Swartz for his suggestions and comments.

Sean B. Palmer