W3C home > Mailing lists > Public > public-rdf-shapes@w3.org > February 2017

SHACL syntax in SHACL

From: Peter F. Patel-Schneider <pfpschneider@gmail.com>
Date: Sat, 18 Feb 2017 19:17:55 -0800
To: Holger Knublauch <holger@topquadrant.com>, public-rdf-shapes@w3.org
Message-ID: <9e1958eb-bbd5-e6b8-c059-f5df5a7e865c@gmail.com>
I spent about an hour yesterday evening looking into building a syntax
checker for SHACL Core in non-recursive SHACL Core.  It turns out that there
is only one problem, and that is checking for loops in property paths and
shapes.  These loops can be checked if sh:disjoint is generalized to
take a path instead of just a property.

So here is a syntax checker for a slightly modified version of non-recursive
SHACL Core in this slightly extended version of non-recursive SHACL Core.
The changes from the current definition of SHACL Core are:
1/ The syntaxt of property paths has been fixed.
2/ There is a simpler setup for shapes, eliminating sh:NodeShape and
   sh:PropertyShape in favour of sh:Shape and replacing sh:node and
   sh:property with sh:shape.
3/ The constraint components using sh:equals, sh:disjoint, sh:lessThan,
   sh:lessThanOrEquals have been extended to permit SHACL paths.
4/ The feature involving sh:disjointSiblings has been removed.
5/ Boolean parameters don't allow multiple values.  Having both true and
   false for a boolean parameter is confusing.
6/ The only lists and property paths that are checked for ill-formedness are
   those that are used by shapes.  Checking other lists and property paths
   is unnecessary.

Well-formed SHACL lists:
1. rdf:nil has no value for rdf:first
2. rdf:nil has no value for rdf:rest
3. Some value for rdf:rest* is rdf:nil
4. Each value for rdf:rest* that is not rdf:nil has single value for
   rdf:first and single value for rdf:rest.

shi:SHACLlist rdf:type sh:Shape ;
  sh:path [ sh:zeroOrMorePath rdf:rest ] ;
  sh:hasValue rdf:nil ;
  sh:or ( [ sh:in ( rdf:nil ) ;
  	    sh:shape [ sh:path rdf:first ; sh:maxCount 0 ] ;
 	    sh:shape [ sh:path rdf:rest ; sh:maxCount 0 ] .
  	  [ sh:shape [ sh:path rdf:first; sh:minCount 1; sh:maxCount 1 ] ;
 	    sh:shape [ sh:path rdf:rest; sh:minCount 1; sh:maxCount 1 ] ] ) .

Well-formed SHACL property paths:
- no loops
- either IRI or blank node
- if blank node exactly one of the following ; if so extra condition
  - has >0 rdf:first or >0 rdf:rest ; is SHACL list of property paths, length > 1
  - has >0 sh:alternativePath; single value is list of property paths, length > 1
  - has >0 sh:inverse; single value is property path
  - has >0 sh:zeroOrMorePath; single value is property path
  - has >0 sh:oneOrMorePath; single value is property path
  - has >0 sh:zeroOrOnePath; single value is property path

_:pathPath sh:alternativePath (
  [ sh:sequencePath ( [ sh:zeroOrMorePath rdf:rest ] rdf:first ) ]
  [ sh:sequencePath ( sh:alternativePath [ sh:zeroOrMorePath rdf:rest ]
rdf:first ) ]
  sh:inverse sh:zeroOrMorePath sh:oneOrMorePath sh:zeroOrOnePath ) .

shi:pPath rdf:type sh:Shape ;
  sh:shape [ sh:path [ sh:zeroOrMorePath _:pathPath ] ; sh:shape
shi:pPathLocal ] ;
  sh:disjoint [ sh:oneOrMorePath _:pathPath ] .  # no loops

shi:pPathLocal rdf:type sh:Shape ;
  sh:or ( [ sh:nodeKind sh:IRI ]
    [ sh:nodeKind sh:BlankNode ;
      sh:shape [ sh:path sh:first ; sh:maxCount 1 ] ;
      sh:shape [ sh:path sh:rest ; sh:maxCount 1 ; sh:shape shi:SHACLlist ;
      	       	 sh:not [ sh:path rdf:rest ; sh:hasValue rdf:nil ] ] ;
      sh:shape [ sh:path sh:alternativePath ; sh:maxCount 1 ;
  	         sh:shape shi:SHACLlist ;
 	         sh:not [ sh:path rdf:resf ; sh:hasValue rdf:nil ] ;
 	     	 sh:not [ sh:path ( rdf:rest rdf:rest ) ; sh:hasValue rdf:nil ] ] ;
      sh:shape [ sh:path sh:inverse ; sh:maxCount 1 ] ;
      sh:shape [ sh:path sh:zeroOrMorePath ; sh:maxCount 1 ] ;
      sh:shape [ sh:path sh:oneOrMorePath ; sh:maxCount 1 ] ;
      sh:shape [ sh:path sh:zeroOrOnePath ; sh:maxCount 1 ] ;
      sh:xone ( [ sh:or ( [ sh:path rdf:first ; sh:minCount 1 ]
 	      	          [ sh:path rdf:rest ; sh:minCount 1 ] ) ] ;
  	        [ sh:path sh:alternativePath ; sh:minCount 1 ] ;
 		[ sh:path sh:inverse ; sh:minCount 1 ] ;
 		[ sh:path sh:zeroOrMorePath ; sh:minCount 1 ] ;
 		[ sh:path sh:oneOrMorePath ; sh:minCount 1 ] ;
 		[ sh:path sh:zeroOrOnePath ; sh:minCount 1 ] ) ] ) .

Shapes:
- SHACL instances of sh:Shape
- subjects of triples with target property as predicate
- subjects of triples with parameter as predicate
- objects of triples with non list-taking, shape-expecting parameter as predicate
- elements of values of triples with list-taking, shape-expecting parameters
as predicate

_:subshapePath sh:alternativePath ( sh:not sh:shape
	( sh:and [ sh:zeroOrMorePath sh:rest] sh:first )
	( sh:or [ sh:zeroOrMorePath sh:rest] sh:first )
	( sh:xone [ sh:zeroOrMorePath sh:rest] sh:first ) ) .

shi:shapes rdf:type sh:Shape ;
  sh:targetClass sh:Shape ;
  sh:targetSubjectsOf sh:targetClass , sh:targetNode , sh:targetSubjectsOf ,
sh:targetObjectsOf ;
  sh:targetSubjectsOf sh:class, sh:datatype, sh:nodeKind, sh:minCount,
sh:maxCount,
  		      sh:minExclusive, sh:minInclusive, sh:maxExclusive, sh:maxInclusive,
		      sh:minLength, sh:maxLength, sh:pattern, sh:flags, sh:languageIn,
sh:uniqueLang,
		      sh:equals, sh:disjoint, sh:lessThan, sh:lessThanOrEquals,
		      sh:qualifiedValueShape, sh:qualifiedMinCount, sh:qualifiedMaxCount,
		      sh:closed, sh:ignoredProperties, sh:in, sh:hasValue ;
  sh:targetObjectsOf sh:not, sh:shape, sh:qualifiedValueShape ;
  sh:shape shi:shape .

shi:shapeLists rdf:type sh:Shape ;
  sh:targetObjectsOf sh:and, sh:or, sh:xone ;
  sh:shape shi:SHACLlist ;
  sh:shape [ sh:path ( [ sh:zeroOrMorePath rdf:rest ] rdf:first ); sh:shape
shi:shape ] .

Well-formed shapes:
- no recursive shapes

_:shapepath sh:alternativePath (
  sh:shape sh:qualifiedValueShape
  ( [ sh:alternativePath ( sh:and sh:or sh:xone ) ] [ sh:zeroOrMorePath
rdf:rest ] rdf:first ) ) .

shi:shape rdf:type sh:Shape ;
  sh:shape [ sh:path [ sh:zeroOrMorePath_:shapepath ] ; shi:shapeLocal ] ;
  sh:disjoint [ sh:oneOrMorePath _:shapepath ] . # remove to allow recursive
shapes

Targets in well-formed shapes:
- sh:targetNode - IRI or literal
- sh:targetClass - IRI
- sh:targetSubjectsOf, sh:targetObjectsOf - IRI

shi:shapeLocal rdf:type sh:Shape ;
  sh:shape [ sh:path sh:targetNode ; sh:nodeKind sh:IRIOrLiteral ] ;
  sh:shape [ sh:path sh:targetClass ; sh:nodeKind sh:IRI ] ;
  sh:shape [ sh:path sh:targetSubjectsOf ; sh:nodeKind sh:IRI ] ;
  sh:shape [ sh:path sh:targetObjectsOf ; sh:nodeKind sh:IRI ] .

Implicit target in well-formed shapes:
- SHACL instance of both sh:Shape and rdfs:Class must be IRI

shi:shapeLocal sh:or ( [ sh:not [ sh:class rdfs:Class ; sh:class sh:Shape ] ] ;
 	       	       [ sh:nodeKind sh:IRI ] ) .

Property values in well-formed shapes:
- sh:Severity - IRI
- sh:deactivated - **at most one** - datatype xsd:boolean
- sh:path - at most one value - SHACL property path
- sh:class - IRI
- sh:datatype - IRI
- sh:nodeKind - sh:IRI, ....
- sh:minCount, sh:maxCount - datatype xsd:integer
- sh:minExclusive,  sh:minInclusive - literal
- sh:maxExclusive,  sh:maxInclusive - literal
- sh:minLength, sh:maxLength - datatype xsd:integer
- sh:pattern, sh:flags - at most one, datatype xsd:string
- sh:languageIn - SHACL list of datatype xsd:string
- sh:uniqueLang - **at most one**, xsd:boolean
- sh:equals, sh:disjoint, sh:lessThan, sh:lessThanOrEquals - path (was IRI)
- sh:not, sh:shape, (sh:node, sh:property) - shape
- sh:and, sh:or, sh:xone - SHACL list of shapes
- sh:qualifiedValueShape - at most one - shape
- sh:qualifiedMaxCount, sh:qualifiedMinCount - at most one - datatype xsd:integer
- sh:in - SHACL list
- sh:hasValue - no restrictions
- sh:closed - at most one, xsd:boolean
- sh:ignoredProperties - at most one, SHACL list of IRI

shi:shapeLocal
  sh:shape [ sh:path sh:Severity ; sh:nodeKind sh:IRI ] ;
  sh:shape [ sh:path sh:deactivated ; sh:maxCount 1; sh:datatype xsd:boolean ] ;
  sh:shape [ sh:path sh:path ; sh:maxCount 1; sh:shape shi:pPath ] ;
  sh:shape [ sh:path sh:class ; sh:nodeKind sh:IRI ] ;
  sh:shape [ sh:path sh:datatype ; sh:nodeKind sh:IRI ] ;
  sh:shape [ sh:path sh:nodeKind ;
  	     sh:in ( sh:BlankNode sh:IRI sh:Literal sh:BlankNodeOrIRI
 	     	     sh:BlankNodeOrLiteral sh:IRIOrLiteral ) ] ;
  sh:shape [ sh:path sh:minCount ; sh:datatype xsd:integer ] ;
  sh:shape [ sh:path sh:maxCount ; sh:datatype xsd:integer ] ;
  sh:shape [ sh:path sh:minExclusive ; sh:nodeKind sh:Literal ] ;
  sh:shape [ sh:path sh:minInclusive ; sh:nodeKind sh:Literal ] ;
  sh:shape [ sh:path sh:maxExclusive ; sh:nodeKind sh:Literal ] ;
  sh:shape [ sh:path sh:maxInclusive ; sh:nodeKind sh:Literal ] ;
  sh:shape [ sh:path sh:minLength ; sh:datatype xsd:integer ] ;
  sh:shape [ sh:path sh:maxLength ; sh:datatype xsd:integer ] ;
  sh:shape [ sh:path sh:pattern ; sh:maxCount 1 ; sh:datatype xsd:string ] ;
  sh:shape [ sh:path sh:flags ; sh:maxCount 1 ; sh:datatype xsd:string ] ;
  sh:shape [ sh:path sh:languageIn ; sh:shape shi:SHACLlist ;
  	     sh:shape [ sh:path ( [ sh:zeroOrMorePath rdf:rest) rdf:first ) ;
 	     	      		    sh:datatype xsd:string ] ] ;
  sh:shape [ sh:path sh:uniqueLang ; sh:maxCount 1; sh:datatype xsd:boolean ] ;
  sh:shape [ sh:path sh:equals ; sh:shape shi:pPath ] ; # was sh:nodeKind sh:IRI
  sh:shape [ sh:path sh:disjoint ; sh:shape shi:pPath ] ; # was sh:nodeKind
sh:IRI
  sh:shape [ sh:path sh:lessThan ; sh:shape shi:pPath ] ; # was sh:nodeKind
sh:IRI
  sh:shape [ sh:path sh:lessThanOrequals ; sh:shape shi:pPath ] ; # was
sh:nodeKind sh:IRI
  sh:shape [ sh:path sh:qualifiedValueShape ; sh:maxCount 1 ] ;
  sh:shape [ sh:path sh:qualifiedMinCount ; sh:maxCount 1 ] ;
  sh:shape [ sh:path sh:qualifiedMaxCount ; sh:maxCount 1 ] ;
  sh:shape [ sh:path sh:closed ; sh:maxCount 1; sh:datatype xsd:boolean ] ;
  sh:shape [ sh:path sh:ignoredProperties ; sh:maxCount 1; sh:shape
shi:SHACLlist ;
  	     sh:shape [ sh:path ( [ sh:zeroOrMorePath sh:rest ] sh:first ) ;
 	     	      		    sh:nodeKind sh:IRI ] ] ;
  sh:shape [ sh:path sh:in ; sh:shape shi:SHACLlist ] .


Peter F. Patel-Schneider
Nuance Communications
Received on Sunday, 19 February 2017 03:18:34 UTC

This archive was generated by hypermail 2.4.0 : Friday, 17 January 2020 17:02:48 UTC