This document describes Shapes Constraint Language (SHACL) Rules.
This specification is published by the Data Shapes Working Group .
This document introduces the concept of inference rules for SHACL 1.2, a mechanism for deriving new RDF triples from existing data using declarative rules defined in shapes graphs. This extends SHACL’s capabilities beyond validation, enabling reasoning and data enrichment.
This document complements other SHACL 1.2 specifications, such as SHACL Core, by defining the syntax and semantics of rule-based inference. While SHACL Core focuses on constraint validation, the SHACL Rules specification provides a standardized way to express and evaluate rules that generate new data.
Connect to definitions in RDF 1.2 Concepts.
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.
Formal definitions appear in blue boxes:
# This box contains textual definitions.
TODO
RFC 2119 language should autmatically be inserted here.
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.
:A :fatherOf :X . :B :motherOf :X . :C :motherOf :A .
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 `:childOf` `:A` and `:B`, and that `:X` is `:descendedFrom` `:C`
Rules may also generate new RDF resources or literals that are not present in the original data graph, thereby extending both the set of triples and the set of nodes. A common way to introduce new nodes is by using the `BIND` function in the rule body to assign the newly created value to a variable that is then referenced in the rule head. New RDF resources can be created with `BIND` in combination with the `BNODE` function, which creates a new blank node, while new literals can be generated using standard SPARQL functions for datatype construction and manipulation.
For example, consider the following data graph, in which `:A` and `:B` are persons, together with the rule below stating that every person has a father who is also a person. The father is represented as an anonymous individual (i.e., a blank node) created using `BNODE`.
:A rdf:type :Person . :B rdf:type :Person .
RULE { ?N :fatherOf ?x. ?N rdf:type :Person } WHERE { ?x rdf:type :Person. BIND(BNODE() AS ?N) }
Inferred data:
_:bo rdf:type :Person . _:bo :fatherOf :A . _:b1 rdf:type :Person . _:b1 :fatherOf :B .
Two new blank nodes are inferred, `_:bo` and `_:b1`, the former representing the father of `:A` and the latter representing the father of `:B`. It remains unknown whether these blank nodes denote the same individual, in which case `:A` and `:B` would be (half-)siblings, or two distinct individuals.
It is important to note that re-executing the rule on the newly inferred data will produce additional blank nodes—for example, fathers for `_:bo` and `_:b1`. If the rule is repeatedly applied in this way until no further triples can be inferred, the process may enter an infinite loop. Handling such situations lies outside the scope of this document. In other words, it is the user's responsibility to iteratively apply rules only under conditions that avoid infinite loops.
# Default value - calculate a name
RULE { ?x :name ?FN } WHERE {
?x rdf:type :Person
NOT EXISTS { ?x :name ?FN }
?x :givenName ?name1 ;
:familyName ?name2 .
BIND(concat(?name1, " ", ?name2) AS ?FN)
}
The Shape Rules Abstract Syntax
?? Abstract Rules Syntax
An [=expression=] is a function, or functional form. An expression is evaluated with respect to a [=solution mapping=] to give an [=RDF term=] result. Expressions are compatible with SHACL list parameter functions and with SPARQL expressions.
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, and position 3 is informally called the object.
Well-formedness is a set of conditions on the abstract syntax of shapes rules. Together, these rules ensure that a [=variable=] in the [=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 introduces a new variable, not used earlier in the rule body.
A [=rule=] is a well-formed rule if all of the following conditions are met:
A [=rule set=] is "well-formed" if and only if all of the [=rules=] of the rule set are "well-formed".
The compact syntax has an equivalent RDF syntax form. Well-formed RDF syntax can be translated to the compact syntax.
Change the name away from "compact" and leave that free for SHACL-CS.
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 ) ]
) ]
]
);
]
) .
Well-formed ness:
Describe how the abstract model maps to triples??. way round - copes with extra triples. Output is the instance of the abtract model that generates 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
The grammar is given below.
Mapping the AST to the abstract syntax.
Additional helpers (short-hand abbreviations) @@which also allow specialised implementations for basic engines@@.
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 outcomes.
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.
μ : V → T,
where V is the set of all variables and T is the set of all RDF terms.
The domain of μ is denoted by dom(μ),
and it is the subset of V for which μ is defined.
We use the term [=solution=] where it is clear that a [=solution mapping=] is meant.
Write μ0 for the solution mapping such that
dom(μ0) is the empty set.
subst(μ, [=triple pattern=]) that returns a triple pattern
where each occurrence in the triple pattern of a variable that is in the dom(μ)
is replaced by the [=RDF Term=] given by the [=solution mapping=] for var.
If the triple pattern result has no variables, then it is an [=RDF Triple=].
Let G be an [=RDF graph=] and TP be a triple pattern. The function `match(G, TP)` returns a set of all possible solutions that, when applied to the triple pattern, produce a triple that is in the [=evaluation graph=]
Let G be an [=RDF graph=] and TP be a triple pattern.
match(G, TP) = { μ | subst(μ, TP) is a triple in G }
Let S1 and S2 be solutions.
compatible(μ1, μ2) = true
if forall v in dom(μ1) intersection dom(μ2)
μ1(v) = μ2(v)
compatible(μ1, μ2) = false otherwise
merge(S1, S2) = { μ |
μ1 in S1, μ2 in S2
and compatible(μ1, μ2)
μ(v) = μ1(v) if v in dom(μ1)
μ(v) = μ2(v) if v in dom(μ2) }
Let F(arg1, arg2, ...) be an expression.
where arg1, arg2 are RDF terms.
Let [x/μ] be
if x is an RDF term, the [x/row] is x
if x is a variable then [x/row] is μ(x)
## 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`
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 sequence of one solution that does not map any variables.
## (This is the join identity)
let SEQ: Solution sequence = { μ0 }
let G = evaluation graph
# Start::
Replace each blank node in B with a fresh
variable that does not occur in the rule body.
# Evaluate rule body
for each rule element rElt in B
let S1 = { }
if rElt is a triple pattern TP:
X = match(G, TP)
SEQ1 = { μ0 }
for each μ1 in X:
for each μ2 in T:
if compatible(μ1, μ2)
μ3 = merge(μ1, μ2)
add μ3 to SEQ1
endfor
endif
if rElt is a condition expression F:
SEQ1 = {}
for each solution μ in SEQ:
## EBV = Effective boolean value.
## TODO: Eval errors.
if ( EBV(eval(μ, F)) is true )
add μ to SEQ1
endif
endfor
endif
if rElt is an assignment(V, expr)
SEQ1 = {}
for each solution S in SEQ:
let x = eval(expr, μ)
add(V, x) to S
add S to SEQ1
endfor
endif
if SEQ1 is empty
SEQ = {}
exit
endif
SEQ = SEQ1
endfor
# Evaluate rule head
let H = empty set
for each μ in T:
Let S = {}
for each triple template TT in head
Let triple = subst(μ, TT)
Add triple to S
H = H union S
endfor
result eval(R, G) is H
Note that `H` may contain triples that are also in the data grapoh.
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
[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 |
[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 ')' |
[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 |
::= | '\' ( '_' | '~' | '.' | '-' | '!' | '$' | '&' | "'" | '(' | ')' | '*' | '+' | ',' | ';' | '=' | '/' | '?' | '#' | '@' | '%' ) |
TODO
TODO
TODO
Many people contributed to this document, including members of the RDF Data Shapes Working Group.