proposed user-friendly syntax for SHACL

  A User-Friendly Syntax for Core SHACL


This is a user-friendly syntax for the core of SHACL that tries to align
directly with my SHACL RDF syntax.


 Example        Description

∈ ex:Person ⊩ |≤27|   # there are at most 27 people
¹ex:child ⊩ ∋ ex:john   # child subjects include John
¹ex:child ⊩ ∈ ex:Person ∧ IRI  # ... are people and not blank
ex:child² ⊩ ∈ ex:Person   # child objects are people
∈ ex:Patriot ⊩ ⋹ ex:Citizen  # patriots are directly citizens
ex:password² ⊩ ^^xs:string ∧ ℓ≤24 ∧ ℓ≥8 # passwords are between 8 and 24 long
ex:age² ⊩ ^^xs:integer ∧ ≥0  # ages are non-negative integers
ex:john ⊩ ex:name ∝ "^John.*" ★  # John's name starts with "John"
ex:mstatus² ⊩ ∈ { ex:single ex:married ex:divorced }
     # three marital statuses only
ex:Person ⊩ ex:mstatus ∝ |=1|  # people have one marital status
ex:Person ⊩ ex:mstatus ∝ ∋ ex:married → ex:spouse ∝ |≥1|
     # married people have a spouse
ex:Person ⊩ ex:mstatus ∝ ∋ ex:single → ex:spouse ∝ |≤0|
     # single people don't have a spouse
∈ ex:Person ⊩ ex:spouse ∅ ex:child ∧ # people can't marry their children
 ex:child ex:age ≤ ex:age ∧ # ... are older than their children
 ex:age ≤ ex:child⁻¹ ex:age ∧ # ... are younger than their parents
 ex:child⁻¹ ex:child ∝ |≤9| ∧ # ... have at most 8 siblings
 ex:name ∝ ∈ rdf:langString ∧ # ... names are lang-tagged strings
 ex:name ∝ ➀   # ... have only one name per language
∈ ex:Person ⊩
  ( ( ex:spouse ∝ |≤0| → ex:mstatus ∝ ( |≥1| ∧ ∈{ex:single ex:divorced} ) ) ∖
    ( ex:spouse ∝ |≤1| → ex:mstatus ∝ ( |≥1| ∧ ∈{ex:married} ) ) ∖
    |≤3| ∖ )        # people with no spouse are single or divorced
    # people with one spouse are married
    # and there are at most three people left over
∈ ex:Isolated ⊩ ⟦ rdf:type ⟧  # isolated nodes have only types
∈ ex:nonIsolated ⊩ ¬ ⟦ rdf:type ⟧ # non-isolated nodes have other values
sh:partShape ≡ (IRI ∨ sh:inverse ∝ IRI) # parts are properties or inverses
sh:pathShape ≡ ∈ sh:path ⊩ ( sh:partShape ∨ ⦇ sh:partShape ⦈ )
     # paths are parts or lists of parts


 Grammar     Meaning

shaclDoc  ::= ( prefixID | definition ) *

prefixID  ::= '@prefix' prefixName ':' IRI

definition::= name '≡' shape   # refer to shape by name
     | name '≡' scopedShape  # refer to shape by name

scopedShape ::= scope ( '∪' scope )* '⊩' shape
  # set of all nodes in any scope validates against shape

scope   ::= value    # the value
     | '∈' class    # SHACL instances of class
     | '¹' property   # subjects of property
     | property '²'   # objects of property
     | '¹' '?'    # all subjects
     | '?' '²'    # all objects

shape   ::= ( filter ( '∧' filter )* '→' )? component ( '∧' component )*
  # set of nodes that validate against all filters
  # validates against each component

filter   ::= component    # nodes that validate vs shape

component ::= name    # validate against named shape
     | '¬' component   # doesn't validate ag. comp.
     | '∈' class ( '∪' class )*  # SHACL instance of some class
     | '^^' datatype ( '∪' datatype )* # has one of datatypes
     | '∈' '{' value* '}'  # is one of values
     | '⋹' class     # has rdf:type of class
     | 'ℓ' '≤' nonnegativeInteger  # maximum string length
     | 'ℓ' '≥' nonnegativeInteger  # minimum string length
     | '>' literal   # exclusive minimum
     | '≥' literal   # inclusive minimum
     | '<' literal   # exclusive maximum
     | '≤' literal   # inclusive maximum
     | 'IRI' | 'Literal' | 'BlankNode' # kind of node
     | regex '★' ( string )?  # matches pattern (with flags)
     | path '=' path   # path values the same
     | path '∅' path   # path values disjoint
     | path '<' path   # path1 values < path2 values
     | path '≤' path   # path1 values ≤ path2 values
     | path '∝' component  # path values in shape
     | '⦇' shape '⦈'   # list members in shape
     | '⟦' pathpart * '⟧'  # no other property has values
     | '∋' value    # set contains value
     | '|' '≥' nonnegativeInteger '|' # minimum size of set
     | '|' '≤' nonnegativeInteger '|' # maximum size of set
     | '|' '=' nonnegativeInteger '|' # exact size of set
     | '➀'    # only one value per language
     | '(' shape ')'   # validate against shape
     | '(' component ('∨'component)+ ')' # validate against one or more
     | '(' ( component '∖' ) + ')' # partition - see below
  # The initial remnant is the entire set being validated.
  # The next remnant is the subset of the current one that fails
  # to validate against the filter of the respective component.
  # The final remnant is empty.
  # Each remnant validates against the respective component.
⦅

path   ::= pathpart +   # composition
pathpart  ::= property
     | property '⁻¹'   # inverse of property

class   ::= name    # a class
datatype  ::= name    # a datatype
property  ::= name    # a property
value   ::= name | literal   # object

regex   ::= string    # regular expression

name      ::= qname   # NOTE: no <IRI> here, just qnames
qname   ::= prefixName ':' name
IRI   ::= '<' ucharacter '>'

prefixName  as per Turtle
name   as per Turtle
literal     as per Turtle

string   as per usual
nonnegativeInteger as per usual



 Possible Tweaks

It would be possible to modify the syntax to come up with a more standard
treatment of conjunction, disjunction, and negation but that syntax would
not align as closely with the RDF syntax.

Received on Friday, 1 April 2016 13:03:07 UTC