This document describes Shapes Constraint Language (SHACL) Inference Rules.

This specification is published by the Data Shapes Working Group .

Introduction

Inference Rules

Terminology

Connect to definitions in RDF 1.2 Concepts.

Document Conventions

Some examples in this document use Turtle [[turtle]]. The reader is expected to be familiar with SHACL [[shacl]] and SPARQL [[sparql-query]].

Within this document, the following namespace prefix bindings are used:

Prefix Namespace
rdf: http://www.w3.org/1999/02/22-rdf-syntax-ns#
rdfs: http://www.w3.org/2000/01/rdf-schema#
sh: http://www.w3.org/ns/shacl#
xsd: http://www.w3.org/2001/XMLSchema#
ex: http://example.com/ns#

Throughout the document, color-coded boxes containing RDF graphs in Turtle will appear. These fragments of Turtle documents use the prefix bindings given above.

        # This box represents a shapes graph
        # This box represents a data graph.
        # This box represents an output results graph

Formal definitions appear in blue boxes:

TEXTUAL DEFINITIONS
          # This box contains textual definitions. 

Grey boxes such as this include syntax rules that apply to the shapes graph.

true denotes the RDF term "true"^^xsd:boolean . false denotes the RDF term "false"^^xsd:boolean .

TODO

RFC 2119 language should autmatically be inserted here.

Outline

SHACL rules infer new triples. The input is a data graph and a shape graph with rules, the output is a graph of inferred triples that do not occur in the data graph.

Data:

   :A :fatherOf :X .
   :B :motherOf :X .
   :C :motherOf :A .

Rules:

RULE { ?x :childOf ?y } WHERE { ?y :fatherOf ?x }
RULE { ?x :childOf ?y } WHERE { ?y :motherOf ?x }

RULE { ?x :descendedFrom ?y } WHERE { ?x :childOf ?y }
RULE { ?x :descendedFrom ?y } WHERE { ?x :childOf ?z . ?z :childOf ?y }

will conclude that: :X is the child of :a and :B. and that :X is descendedFrom :C

Shape Rules Data Model

The Shape Rules Data Model

"Abstract syntax" and "data model" are synonymous so this could be "Rules Abstract Syntax". "Rules Data model" has the advantage that "syntax" is left for the concrete syntaxes. Downside - it's "rules data" not "data".

variable
[ sh:var "name" ]
node expression
Link to shacl12-node-expr#? -- needs to be single value, list arguments. Often called an expression.
data block
A data block is a set of triples. These form extra facts that are included in the inference process.
triple template
A triple template is 3-tuple where each element is either a variable or an RDF term (including being a triple term). [=Triple templates=] appear in the [=head=] of a [=rule=].
triple pattern
A triple pattern is 3-tuple where each element is either a variable, or an RDF term (including being a triple term). [=Triple patterns=] appear in the [=body=] of a [=rule=].
condition expression
A condition expression is a function, or functional form, that evaluates to true or false. [=Condition expressions=] appear in the [=body=] of a [=rule=].
assignment
An assignment is a pair of a variable, called the assignment variable, and an expression, called the assignment expression. [=Assignments=] appear in the [=body=] of a [=rule=].
rule
A rule is a pair of a [=rule head=] (often just "head") and a [=rule body=] (often just "body").
rule head
A rule head is a sequence where each element of the sequence is a [=triple template=].
rule body
A rule body is a sequence where each element of the sequence is a [=triple pattern=], a [=condition expression=] or an [=assignment=].
rule set
A rule set is a collection of zero or more rules.

In a [=triple pattern=] or a [=triple template=], position 1 of the tuple is informally called the subject; position 2 is informally called the predicate position 3 is informally called the object.

Well-formedness Conditions

Well-formedness is a set of conditions on abstract data model of shapes rules. Together, these rules ensure that a [=variable=] in a [=head=] of a rule has a value defined in the [=body=] of the rule; that each variable in an condition expression or assignment expression has a value at the point of evaluation, and that each assignment in a rule only introduces a new variable, not used earlier in the rule body.

A [=rule=] is a well-formed rule if all the following conditions are met:

A [=rule set=] is "well-formed" if and only all the [=rules=] of the rule set are "well-formed".

Concrete Syntax forms for Shapes Rules

The compact syntax has an equivalent RDF syntax form.
Well-formed RDF syntax can be translated to the compact syntax.

Compact:

PREFIX : <http://example/>

DATA { :x :p 1 ; :q 2 . }

RULE { ?x :bothPositive true . } 
WHERE { ?x :p ?v1  FILTER ( ?v1 > 0 )  ?x :q ?v2  FILTER ( ?v2 > 0 )  }

RULE { ?x :oneIsZero true . } 
WHERE { ?x :p ?v1 ;  :q ?v2  FILTER ( ( ?v1 = 0 ) || ( ?v2 = 0 ) )  }

RDF:

PREFIX :       <http://example/>
PREFIX rdf:    <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX sh:     <http://www.w3.org/ns/shacl#>
PREFIX sparql: <http://www.w3.org/ns/sparql#>

:ruleSet-1
  rdf:type sh:RuleSet;
  sh:data (
    <<( :x :p 1 )>>
    <<( :x :q 2 )>>
  );
  sh:ruleSet (
    [
      rdf:type sh:Rule;
      sh:head (
        [ sh:subject [ sh:var "x" ] ; sh:predicate :bothPositive; sh:object true ]
      )
      sh:body (
        [ sh:subject [ sh:var "x" ]; sh:predicate :p; sh:object [ sh:var "v1" ] ]
        [ sh:expr [ sparql:greaterThan ( [ sh:var "v1" ] 0 ) ] ]
        [ sh:subject [ sh:var "x" ] ; sh:predicate :q; sh:object [ sh:var "v2" ] ]
        [ sh:expr [ sparql:greaterThan ( [ sh:var "v2" ] 0 ) ] ]
      );
    ]
    [
      rdf:type sh:Rule;
      sh:head (
        [ sh:subject [ sh:var "x" ] ; sh:predicate :oneIsZero ; sh:object true ]
      )
      sh:body (
        [ sh:subject [ sh:var "x" ] ; sh:predicate :p ; sh:object [ sh:var "v1" ] ]
        [ sh:subject [ sh:var "x" ] ; sh:predicate :q ; sh:object [ sh:var "v2" ] ]
        [ sh:expr [ sparql:function-or (
              [ sparql:equals ( [ sh:var "v1" ] 0 ) ]
              [ sparql:equals ( [ sh:var "v2" ] 0 ) ]
            ) ]
        ]
      );
    ]
  ) .

RDF Rules Syntax

Well -formed ness:

Describe how the data model maps to triples??. way round - copes with extra triples. Output is the instance of the dada model that generate the triples - but need to define "maximal".

Process : accumulators, bottom up/ Walk the structure.

All triples not in the syntax are ignored. No other "sh:" predicates are allowed (??).

@@ link to SHACL constraints@@

Compact Rules Syntax

The grammar is given below.

Mapping the AST to the abstrat data model.

Compact Syntax Abbreviations

Additional helpers (short-hand abbreviations) @@which also allow specialised implementations for basic engines@@.

  • `TRANSITIVE`
  • `SYMMETRIC`
  • `INVERSE`

Shape Rules Evaluation

This section defines the outcome of evaluating a rule set on given data. It does not prescribe the algorithm as the method of implementation. An implementation can use any algorithm that generates the same outcome.

Inputs: data graph G and a rule set RS.
Output: an RDF graph GI of inferred triples

The inferred triples does not include any triple present in the set of triples of G.

Evaluation of an expression


Let F(arg1, arg2, ...) be an expression.
where arg1, arg2 are RDF terms.

Let [x/row] be
    if x is an RDF term, the [x/row] is x
    if x is a variable then [x/row] is the value of x in the row 
    ## By well-formedness, it is an error if x is not in the row.

eval(F(expr1, expr2), row) = F(eval(expr1, row), eval(expr2, row))
eval(FF(expr1, expr2) , row) = ... things that are not functions like IF

Evaluation of a rule

let R be a well-formed rule.
let rule R = (head, body) where
             H is the sequence of triple templates in the head
             B is the sequence of triple patterns, condition expressions,
                and assignments in the body

let R : map variable to RDF term as a set of pairs (variable, RDF term)

# "solution"
let T :list of B

Initial T = empty list

# Evaluate rule body
for each rule element rElt:
    if rElt is a triple pattern:
        T1 = empty list
    for each row in T:
      T1 = empty list
      for each matches m set of (var,term)
          row1 = row union (var, term)
          add row1 to T1
          endfor
    T = T1
    endif   

    if rElt is a condition expression F:
        T1 = empty list
        for each row in T:
            if ( eval(F,B) )
                add R to T1
                endif   
        endfor
    T = T1
    endif

    if rElt is an assignment(V, expr)
        T1 = empty list
        for each row in T:
            let x = eval(expr, row)
            let R1 = R union (V, x)
            add R1 to T1
            endfor
        T = T1
        endif
    endfor

# Evaluate rule head
let H = empty set
for each R in T:
    Let S = set of triples obtained by replacing variables 
            in the triple templates of the head with values from R
    define [head(R)/row]
    H = H union S
    endfor

result eval(R, G) is H

Note that `H` may contain triples that are also in the data grapoh.

Evaluation of a Rule Set

Let G be the input RDF graph.
Let I be the set of triples generated by evaluation
Let RS be a rule set
Let finished = false

while !finished:
    finished = true
    foreach rule in RS
        let GI = G union I
        let X = eval(rule, GI)
        let Y = those triples in X that are not in GI
        if Y is not empty:
            finished = false
            endif
        let I = Y union I
        endfor
    endwhile

result is GI

Shapes Rules Language Grammar

[1]   RuleSet   ::=   ( Prologue ( Rule | Data ) )*
[2]   Rule   ::=   Rule1 | Rule2 | Rule3 | Declaration
[3]   Rule1   ::=   'RULE' HeadTemplate 'WHERE' BodyPattern
[4]   Rule2   ::=   'IF' BodyPattern 'THEN' HeadTemplate
[5]   Rule3   ::=   HeadTemplate ':-' BodyPattern
[6]   Declaration   ::=   ( 'TRANSITIVE' '(' iri ')' | 'SYMMETRIC' '(' iri ')' | 'INVERSE' '(' iri ',' iri ')' )
[7]   Data   ::=   'DATA' TriplesTemplateBlock
[8]   HeadTemplate   ::=   TriplesTemplateBlock
[9]   BodyPattern   ::=   '{' BodyPattern1? Assignment* '}'
[10]   BodyPattern1   ::=   ( Filter | BodyPatternSub ( Filter BodyPatternSub? )* )
[11]   BodyPatternSub   ::=   TriplesBlock
[12]   Prologue   ::=   ( BaseDecl | PrefixDecl | VersionDecl | ImportsDecl )*
[13]   BaseDecl   ::=   'BASE' IRIREF
[14]   PrefixDecl   ::=   'PREFIX' PNAME_NS IRIREF
[15]   VersionDecl   ::=   'VERSION' VersionSpecifier
[16]   VersionSpecifier   ::=   STRING_LITERAL1 | STRING_LITERAL2
[17]   ImportsDecl   ::=   'IMPORTS' iri
[18]   TriplesTemplateBlock   ::=   '{' TriplesTemplate? '}'
[19]   TriplesTemplate   ::=   TriplesSameSubject ( '.' TriplesTemplate? )?
[20]   TriplesBlock   ::=   TriplesSameSubjectPath ( '.' TriplesBlock? )?
[21]   ReifiedTripleBlock   ::=   ReifiedTriple PropertyList
[22]   ReifiedTripleBlockPath   ::=   ReifiedTriple PropertyListPath
[23]   Assignment   ::=   Bind
[24]   Bind   ::=   'BIND' '(' Expression 'AS' Var ')'
[25]   Let   ::=   'LET' '(' Var ':=' Expression ')'
[26]   Reifier   ::=   '~' VarOrReifierId?
[27]   VarOrReifierId   ::=   Var | iri | BlankNode
[28]   Filter   ::=   'FILTER' Constraint
[29]   Constraint   ::=   BrackettedExpression | BuiltInCall | FunctionCall
[30]   FunctionCall   ::=   iri ArgList
[31]   ArgList   ::=   NIL | '(' Expression ( ',' Expression )* ')'
[32]   ExpressionList   ::=   NIL | '(' Expression ( ',' Expression )* ')'
[33]   TriplesSameSubject   ::=   VarOrTerm PropertyListNotEmpty | TriplesNode PropertyList | ReifiedTripleBlock
[34]   PropertyList   ::=   PropertyListNotEmpty?
[35]   PropertyListNotEmpty   ::=   Verb ObjectList ( ';' ( Verb ObjectList )? )*
[36]   Verb   ::=   VarOrIri | 'a'
[37]   ObjectList   ::=   Object ( ',' Object )*
[38]   Object   ::=   GraphNode Annotation
[39]   TriplesSameSubjectPath   ::=   VarOrTerm PropertyListPathNotEmpty | TriplesNodePath PropertyListPath | ReifiedTripleBlockPath
[40]   PropertyListPath   ::=   PropertyListPathNotEmpty?
[41]   PropertyListPathNotEmpty   ::=   ( VerbPath | VerbSimple ) ObjectListPath ( ';' ( ( VerbPath | VerbSimple ) ObjectListPath )? )*
[42]   VerbPath   ::=   Path
[43]   VerbSimple   ::=   Var
[44]   ObjectListPath   ::=   ObjectPath ( ',' ObjectPath )*
[45]   ObjectPath   ::=   GraphNodePath AnnotationPath
[46]   Path   ::=   PathSequence
[47]   PathSequence   ::=   PathEltOrInverse ( '/' PathEltOrInverse )*
[48]   PathEltOrInverse   ::=   PathElt | '^' PathElt
[49]   PathElt   ::=   PathPrimary
[50]   PathPrimary   ::=   iri | 'a' | '(' Path ')'
[51]   TriplesNode   ::=   Collection | BlankNodePropertyList
[52]   BlankNodePropertyList   ::=   '[' PropertyListNotEmpty ']'
[53]   TriplesNodePath   ::=   CollectionPath | BlankNodePropertyListPath
[54]   BlankNodePropertyListPath   ::=   '[' PropertyListPathNotEmpty ']'
[55]   Collection   ::=   '(' GraphNode+ ')'
[56]   CollectionPath   ::=   '(' GraphNodePath+ ')'
[57]   AnnotationPath   ::=   ( Reifier | AnnotationBlockPath )*
[58]   AnnotationBlockPath   ::=   '{|' PropertyListPathNotEmpty '|}'
[59]   Annotation   ::=   ( Reifier | AnnotationBlock )*
[60]   AnnotationBlock   ::=   '{|' PropertyListNotEmpty '|}'
[61]   GraphNode   ::=   VarOrTerm | TriplesNode | ReifiedTriple
[62]   GraphNodePath   ::=   VarOrTerm | TriplesNodePath | ReifiedTriple
[63]   VarOrTerm   ::=   Var | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | NIL | TripleTerm
[64]   ReifiedTriple   ::=   '<<' ReifiedTripleSubject Verb ReifiedTripleObject Reifier? '>>'
[65]   ReifiedTripleSubject   ::=   Var | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | ReifiedTriple
[66]   ReifiedTripleObject   ::=   Var | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | ReifiedTriple | TripleTerm
[67]   TripleTerm   ::=   '<<(' TripleTermSubject Verb TripleTermObject ')>>'
[68]   TripleTermSubject   ::=   Var | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode
[69]   TripleTermObject   ::=   Var | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | TripleTerm
[70]   TripleTermData   ::=   '<<(' TripleTermDataSubject ( iri | 'a' ) TripleTermDataObject ')>>'
[71]   TripleTermDataSubject   ::=   iri | RDFLiteral | NumericLiteral | BooleanLiteral
[72]   TripleTermDataObject   ::=   iri | RDFLiteral | NumericLiteral | BooleanLiteral | TripleTermData
[73]   VarOrIri   ::=   Var | iri
[74]   Var   ::=   VAR1 | VAR2
[75]   Expression   ::=   ConditionalOrExpression
[76]   ConditionalOrExpression   ::=   ConditionalAndExpression ( '||' ConditionalAndExpression )*
[77]   ConditionalAndExpression   ::=   ValueLogical ( '&&' ValueLogical )*
[78]   ValueLogical   ::=   RelationalExpression
[79]   RelationalExpression   ::=   NumericExpression ( '=' NumericExpression | '!=' NumericExpression | '<' NumericExpression | '>' NumericExpression | '<=' NumericExpression | '>=' NumericExpression | 'IN' ExpressionList | 'NOT' 'IN' ExpressionList )?
[80]   NumericExpression   ::=   AdditiveExpression
[81]   AdditiveExpression   ::=   MultiplicativeExpression ( '+' MultiplicativeExpression | '-' MultiplicativeExpression | ( NumericLiteralPositive | NumericLiteralNegative ) ( ( '*' UnaryExpression ) | ( '/' UnaryExpression ) )* )*
[82]   MultiplicativeExpression   ::=   UnaryExpression ( '*' UnaryExpression | '/' UnaryExpression )*
[83]   UnaryExpression   ::=     '!' PrimaryExpression
| '+' PrimaryExpression
| '-' PrimaryExpression
| PrimaryExpression
[84]   PrimaryExpression   ::=   BrackettedExpression | BuiltInCall | iriOrFunction | RDFLiteral | NumericLiteral | BooleanLiteral | Var | ExprTripleTerm
[85]   ExprTripleTerm   ::=   '<<(' ExprTripleTermSubject Verb ExprTripleTermObject ')>>'
[86]   ExprTripleTermSubject   ::=   iri | RDFLiteral | NumericLiteral | BooleanLiteral | Var
[87]   ExprTripleTermObject   ::=   iri | RDFLiteral | NumericLiteral | BooleanLiteral | Var | ExprTripleTerm
[88]   BrackettedExpression   ::=   '(' Expression ')'
[89]   BuiltInCall   ::=     'STR' '(' Expression ')'
| 'LANG' '(' Expression ')'
| 'LANGMATCHES' '(' Expression ',' Expression ')'
| 'LANGDIR' '(' Expression ')'
| 'DATATYPE' '(' Expression ')'
| 'BOUND' '(' Var ')'
| 'IRI' '(' Expression ')'
| 'URI' '(' Expression ')'
| 'BNODE' ( '(' Expression ')' | NIL )
| 'RAND' NIL
| 'ABS' '(' Expression ')'
| 'CEIL' '(' Expression ')'
| 'FLOOR' '(' Expression ')'
| 'ROUND' '(' Expression ')'
| 'CONCAT' ExpressionList
| 'SUBSTR' '(' Expression ',' Expression ( ',' Expression )? ')'
| 'STRLEN' '(' Expression ')'
| 'REPLACE' '(' Expression ',' Expression ',' Expression ( ',' Expression )? ')'
| 'UCASE' '(' Expression ')'
| 'LCASE' '(' Expression ')'
| 'ENCODE_FOR_URI' '(' Expression ')'
| 'CONTAINS' '(' Expression ',' Expression ')'
| 'STRSTARTS' '(' Expression ',' Expression ')'
| 'STRENDS' '(' Expression ',' Expression ')'
| 'STRBEFORE' '(' Expression ',' Expression ')'
| 'STRAFTER' '(' Expression ',' Expression ')'
| 'YEAR' '(' Expression ')'
| 'MONTH' '(' Expression ')'
| 'DAY' '(' Expression ')'
| 'HOURS' '(' Expression ')'
| 'MINUTES' '(' Expression ')'
| 'SECONDS' '(' Expression ')'
| 'TIMEZONE' '(' Expression ')'
| 'TZ' '(' Expression ')'
| 'NOW' NIL
| 'UUID' NIL
| 'STRUUID' NIL
| 'MD5' '(' Expression ')'
| 'SHA1' '(' Expression ')'
| 'SHA256' '(' Expression ')'
| 'SHA384' '(' Expression ')'
| 'SHA512' '(' Expression ')'
| 'COALESCE' ExpressionList
| 'IF' '(' Expression ',' Expression ',' Expression ')'
| 'STRLANG' '(' Expression ',' Expression ')'
| 'STRLANGDIR' '(' Expression ',' Expression ',' Expression ')'
| 'STRDT' '(' Expression ',' Expression ')'
| 'sameTerm' '(' Expression ',' Expression ')'
| 'isIRI' '(' Expression ')'
| 'isURI' '(' Expression ')'
| 'isBLANK' '(' Expression ')'
| 'isLITERAL' '(' Expression ')'
| 'isNUMERIC' '(' Expression ')'
| 'hasLANG' '(' Expression ')'
| 'hasLANGDIR' '(' Expression ')'
| 'REGEX' '(' Expression ',' Expression ( ',' Expression )? ')'
| 'isTRIPLE' '(' Expression ')'
| 'TRIPLE' '(' Expression ',' Expression ',' Expression ')'
| 'SUBJECT' '(' Expression ')'
| 'PREDICATE' '(' Expression ')'
| 'OBJECT' '(' Expression ')'
[90]   iriOrFunction   ::=   iri ArgList?
[91]   RDFLiteral   ::=   String ( LANG_DIR | '^^' iri )?
[92]   NumericLiteral   ::=   NumericLiteralUnsigned | NumericLiteralPositive | NumericLiteralNegative
[93]   NumericLiteralUnsigned   ::=   INTEGER | DECIMAL | DOUBLE
[94]   NumericLiteralPositive   ::=   INTEGER_POSITIVE | DECIMAL_POSITIVE | DOUBLE_POSITIVE
[95]   NumericLiteralNegative   ::=   INTEGER_NEGATIVE | DECIMAL_NEGATIVE | DOUBLE_NEGATIVE
[96]   BooleanLiteral   ::=   'true' | 'false'
[97]   String   ::=   STRING_LITERAL1 | STRING_LITERAL2 | STRING_LITERAL_LONG1 | STRING_LITERAL_LONG2
[98]   iri   ::=   IRIREF | PrefixedName
[99]   PrefixedName   ::=   PNAME_LN | PNAME_NS
[100]   BlankNode   ::=   BLANK_NODE_LABEL | ANON

Productions for terminals:

[101]   IRIREF   ::=   '<' ([^<>"{}|^`\]-[#x00-#x20])* '>'
[102]   PNAME_NS   ::=   PN_PREFIX? ':'
[103]   PNAME_LN   ::=   PNAME_NS PN_LOCAL
[104]   BLANK_NODE_LABEL   ::=   '_:' ( PN_CHARS_U | [0-9] ) ((PN_CHARS|'.')* PN_CHARS)?
[105]   VAR1   ::=   '?' VARNAME
[106]   VAR2   ::=   '$' VARNAME
[107]   LANG_DIR   ::=   '@' [a-zA-Z]+ ('-' [a-zA-Z0-9]+)* ('--' [a-zA-Z]+)?
[108]   INTEGER   ::=   [0-9]+
[109]   DECIMAL   ::=   [0-9]* '.' [0-9]+
[110]   DOUBLE   ::=   ( ([0-9]+ ('.'[0-9]*)? ) | ( '.' ([0-9])+ ) ) [eE][+-]?[0-9]+
[111]   INTEGER_POSITIVE   ::=   '+' INTEGER
[112]   DECIMAL_POSITIVE   ::=   '+' DECIMAL
[113]   DOUBLE_POSITIVE   ::=   '+' DOUBLE
[114]   INTEGER_NEGATIVE   ::=   '-' INTEGER
[115]   DECIMAL_NEGATIVE   ::=   '-' DECIMAL
[116]   DOUBLE_NEGATIVE   ::=   '-' DOUBLE
[117]   STRING_LITERAL1   ::=   "'" ( ([^#x27#x5C#xA#xD]) | ECHAR )* "'"
[118]   STRING_LITERAL2   ::=   '"' ( ([^#x22#x5C#xA#xD]) | ECHAR )* '"'
[119]   STRING_LITERAL_LONG1   ::=   "'''" ( ( "'" | "''" )? ( [^'\] | ECHAR ) )* "'''"
[120]   STRING_LITERAL_LONG2   ::=   '"""' ( ( '"' | '""' )? ( [^"\] | ECHAR ) )* '"""'
[121]   ECHAR   ::=   '\' [tbnrf\"']
[122]   NIL   ::=   '(' WS* ')'
[123]   WS   ::=   #x20 | #x9 | #xD | #xA
[124]   ANON   ::=   '[' WS* ']'
[125]   PN_CHARS_BASE   ::=   [A-Z] | [a-z] | [#x00C0-#x00D6] | [#x00D8-#x00F6] | [#x00F8-#x02FF] | [#x0370-#x037D] | [#x037F-#x1FFF] | [#x200C-#x200D] | [#x2070-#x218F] | [#x2C00-#x2FEF] | [#x3001-#xD7FF] | [#xF900-#xFDCF] | [#xFDF0-#xFFFD] | [#x10000-#xEFFFF]
[126]   PN_CHARS_U   ::=   PN_CHARS_BASE | '_'
[127]   VARNAME   ::=   ( PN_CHARS_U | [0-9] ) ( PN_CHARS_U | [0-9] | #x00B7 | [#x0300-#x036F] | [#x203F-#x2040] )*
[128]   PN_CHARS   ::=   PN_CHARS_U | '-' | [0-9] | #x00B7 | [#x0300-#x036F] | [#x203F-#x2040]
[129]   PN_PREFIX   ::=   PN_CHARS_BASE ((PN_CHARS|'.')* PN_CHARS)?
[130]   PN_LOCAL   ::=   (PN_CHARS_U | ':' | [0-9] | PLX ) ((PN_CHARS | '.' | ':' | PLX)* (PN_CHARS | ':' | PLX) )?
[131]   PLX   ::=   PERCENT | PN_LOCAL_ESC
[132]   PERCENT   ::=   '%' HEX HEX
[133]   HEX   ::=   [0-9] | [A-F] | [a-f]
[134]   PN_LOCAL_ESC   ::=   '\' ( '_' | '~' | '.' | '-' | '!' | '$' | '&' | "'" | '(' | ')' | '*' | '+' | ',' | ';' | '=' | '/' | '?' | '#' | '@' | '%' )

Security Considerations

TODO

Privacy Considerations

TODO

Internationalization Considerations

TODO

Acknowledgements

Many people contributed to this document, including members of the RDF Data Shapes Working Group.