This document defines SHACL Rules.
SHACL, the Shapes Constraint Language, is a language for describing the structure of RDF graphs. SHACL may be used for a variety of purposes such as validating, inferencing, modeling domains, generating ontologies to inform other agents, building user interfaces, generating code, and integrating data.
SHACL Rules provides inferencing with the generation of new RDF data from a combination of a set of rules and a base data graph. Rules can be expressed as RDF or in the SHACL Rules Language (SRL).
This specification is published by the Data Shapes Working Group.
This document introduces inference rules for SHACL 1.2, a mechanism for deriving new RDF triples from existing RDF data through declarative rules. The document defines the syntax and semantics of rule-based inference.
Implementations of SHACL Rules provide two operations. The infer operation that applies the rules to a given base graph and produces an inference graph containing the RDF triples derived by rule execution. Combining the inference graph with the base graph is optional and left to users. The query operation determines whether a given goal pattern can be derived from the base graph using the rules
The inference graph may contain RDF triples with IRIs, blank nodes or literals that were not present in the base graph. Users are responsible to ensure that iterative applications of rules that generate new blank nodes or literals do not result in infinite loops.
SHACL Rules also support constructs, such as negation as failure, that could lead to different inferred graphs depending on the order in which rules are executed. To avoid this, rules are evaluated using the technique of stratification, which establishes a single, implicit ordering among rules, ensuring that the same inference graph is always produced.
Connect to definitions in RDF 1.2 Concepts.
The following definitions from other specifications are used in this document: @@
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# |
srl: |
http://www.w3.org/ns/shacl-rules# |
shnex: |
http://www.w3.org/ns/shacl-node-expr# |
xsd: |
http://www.w3.org/2001/XMLSchema# |
ex: |
http://example.com/ |
Throughout the document, color-coded boxes containing RDF graphs in Turtle will appear. These fragments of Turtle documents use the prefix bindings given above.
TODO
RFC 2119 language should be automatically inserted here.
SHACL rules infer new triples based on a [=base graph=] and a [=rule set=]. The output of evaluation is an [=inference graph=] containing the derived triples that do not appear in the base graph.
Each [=rule=] has a pattern called the [=body=] and a result template called the [=head=]. A rule is executed by finding the values for variables in the body so that the body matches triples from the combined base graph and inferred triples from the execution so far. These values are then used to instantiate the triple templates in the rule head to produce new inferred triples.
The rules are executed until no more triples are inferred and rules may be executed more than once as new inferred triples become available. SHACL Rules execution is defined so that creating new RDF terms, including new blank nodes, and testing for the absence of a pattern do not lead to different inference graphs due to the order of rule execution.
In this first example, we have the following data graph and rule set:
: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 }
The above rules, applied to the data, will conclude that: `:X` is the `:childOf` `:A` and `:B`:
:X :childOf :A . :X :childOf :B . :A :childOf :C .
We can then derive `:descendedFrom` relationships by adding a rule that depends on `:childOf` triples produced by the other 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 }
The outcome is:
:A :descendedFrom :C . :X :descendedFrom :B . :X :descendedFrom :A . :X :childOf :B . :X :childOf :A . :A :childOf :C .
Moreover, we can add a rule that depends on `:descendedFrom` triples to infer that `:X` is `:descendedFrom` `:C`:
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 :descendedFrom ?y }
giving:
:A :descendedFrom :C . :X :descendedFrom :C . :X :descendedFrom :B . :X :descendedFrom :A . :X :childOf :B . :X :childOf :A . :A :childOf :C .
This adds the triple `:X :descendedFrom :C`.
This last rule is a recursive rule, the body of the rule depends on the head of the rule.
We can also use expressions in the body of rules to filter results. For example, to add a class to towns with a population greater than 1500:
:town1 :population 1000 . :town2 :population 2000 .
RULE { ?x rdf:type :largeTown } WHERE { ?x :population ?p . FILTER(?p > 1500) }
:town2 rdf:type :largeTown .
`FILTER` evaluates an expression and keeps the current set of variable bindings if the expression evaluates to true and discards the current set of variable bindings if the expression evaluates to false. This is the same as the `FILTER` operation of SPARQL and SHACL Rules provides many of the same expressions and operators as SPARQL.
Negation allows you to specify a pattern that must not match for a rule to apply. This is called "negation as failure".
In order to evaluate a negation element, the rules evaluation ensures that all the rules that could produce triples matching the pattern in the negation element have been completed. This is called [=stratification=] and it ensures that the negation is based on all the relevant possible triples, whether from the data or inferred, and is not affected by the order of rule execution.
:X1 rdf:type :Place ;
:population 1000 .
:X2 rdf:type :Place ;
:population 2000 .
:X3 rdf:type :Place .
RULE { ?x rdf:type :UnclassifiedSize } WHERE {
?x rdf:type :Place .
NOT { ?x :population ?p . }
}
:X3 rdf:type :UnclassifiedSize .
@@Need example where some of the NOT is inferred triples.
A SHACL [=rule set=] can incorporate other rule sets by including their URLs in the [=rule imports=] of the rule set. This allows rules to be structured into modules and shared across rule sets.
The `IMPORTS` statements of a rule set are processed before any of the rules in the rule set are evaluated. During the importing step, if an imported rule set has its own imports, those are also processed recursivly. Traversing `IMPORTS` statement processes rule sets leadin to cyclic import statements once only; cycles in the import statements graph does not lead to infinite loops.
Assignment allows you to assign the result of an expression to a variable in the body of a rule. This can be used to create new RDF terms based on the data.
RULE { ?x :distanceKm ?kilometers }
WHERE {
?x :distanceMiles ?miles .
SET ( ?kilometers := ?miles * 1.60934 )
}
This can be combined with testing for the absence of a triple already recording the distance in kilometers.
RULE { ?x :distanceKm ?kilometers }
WHERE {
?x :distanceMiles ?miles
NOT { ?x :distanceKm ?km }
SET ( ?kilometers := ?miles * 1.60934 )
}
Rules involving assignment are [=run-once rules=]. Runs with assignment are run after all the rules that could produce the data that they depend on and before any rules that depend on the data they produce.
This condition ensures that rules with assignment do not generate new RDF terms multiple times, with potetially different outcomes, and the order of execution does not affect the resulting inference graph.
At risk:
Rule tuples are disjoint from triples. They are tuples of RDF terms (no variables) and exist only during evaluation of a rule set. They can be used to record intermediate results during rule evaluation and to pass data between rules.
Syntax of tuple patterns, templates and tuples:
Often, the first argument will be a fixed name.
There is a tuple store which holds tuples for the lifetime of the evaluation. The tuple store holds duplicate data tuples (unlike an RDF graph which is a set).
Allow one or more SHACL Rule to be attached to a shape, using a property such as `sh:rule`.
Sketch:
[] rdf:type sh:NodeShape ;
sh:rule ## Different property?
[ a srl:SHACLRule ;
srl:ruleSet "..."; ## SRL syntax
sh:prefixes ... ;
];
[] rdf:type sh:NodeShape ;
sh:rule
[ a srl:SHACLRule ;
srl:ruleSet [ ... RDF syntax ... ] ;
];
SHACL Rules and SPARQL have a close relationship. SHACL Rules are designed to be compatible with SPARQL, and many of the constructs in SHACL Rules are inspired by SPARQL. However, there are some differences:
The Shape Rules Abstract Syntax
An [=expression=] is a function or a functional form. It's arguments are [=RDF terms=]. An expression is evaluated with respect to a [=solution mapping=] to give an [=RDF term=] as the 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 SHACL rules. Together, these conditions 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 element=] or [=assignment expression=] has a value at the point of evaluation; and that each assignment in a rule introduces a new variable, one that has not been 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 following algorithm gives one possible method for constructing the [=dependency graph=] from a [=rule set=]. Conformance depends on the dependency definitions in this section, not on use of this procedure.
## output -- Dependency graph with rule vertices and labeled edges.
define buildDependencyGraph(ruleSet):
## edgeLabelMap maps (R1, R2) to "positive" or "negative"
let edgeLabelMap be a map from (rule, rule) to label
define mergeLabel(oldLabel, newLabel):
## Negative dependency dominates positive dependency.
if oldLabel == "negative" or newLabel == "negative":
return "negative"
else:
return "positive"
endif
enddefine
## Classify all triple patterns in a rule, looking inside negation elements.
foreach rule R1 in ruleSet:
let bodyDependencies = {}
foreach rule body element RBE in the body of R1:
if RBE is a negation element:
foreach triple pattern element TPE in RBE:
let item be a pair (TPE, "negative")
add item to bodyDependencies
endfor
else if RBE is a triple pattern element:
let item be a pair (RBE, "postive")
add item to bodyDependencies
else if RBE is a condition element:
## Do nothing
else if RBE is an assignment element:
## Do nothing
endif
endfor
foreach pair ( triple pattern TP, depLabel) in bodyDependencies:
foreach rule R2 in ruleSet:
foreach triple template TT in head of R2:
## "possibly generate" / matching is defined in
## 3.3 Rule Dependency.
if TT can possibly generate a triple pattern TP:
let key = (R1, R2)
if edgeLabelMap contains key:
let oldLabel = edgeLabelMap.get(key)
let merged = mergeLabel(oldLabel, depLabel)
edgeLabelMap.set(key, merged)
else:
edgeLabelMap.set(key, depLabel)
endif
endif
endfor
endfor
endfor
endfor
let DP = { }
foreach entry ((R1, R2), label) in edgeLabelMap:
DP = { edge (R1 -> R2) labeled label } ∪︀ DP
endfor
the result is DP
enddefine
Notes:
A [=triple template=] with components `ts`, `tp`, `to` can possibly generate a triple with component RDF terms `s`, `p`, `o` if `ts` is a variable or `ts` is the same RDF term as `s`, `tp` is a variable or `tp` is the same RDF term as `p`, and `to` is a variable or `to` is the same RDF term as `o`.
In addition, if any pair of `ts`, `tp`, and `to` are the same variable, then the corresponding pair of `s`, `p`, and `o` must be the same.
Revise
Examples:
@@ Examples of triple patttern dependencies.
@@ Examples of rule dependencies.
[=Stratification=] is the process of partitioning a [=rule set=] into an ordered sequence of [=stratification layers=] (also known as "strata", singular "stratum"), forming a [=stratification=]. Rules in lower [=strata=] are evaluated before rules in higher [=strata=].
[=Stratification=] imposes constraints on dependencies between [=rules=] to ensure that [=negation elements=] depend only on results computed using earlier (lower) [=strata=]. This guarantees a single well-defined outcome from the evaluation of a [=rule set=] over a given [=base graph=].
A stratification process may also be used to make other evaluation decisions. This document describes the necessary conditions for consistent evaluation and gives one possible way to form a stratification. Implementations need to meet the conditions described here in order to get compatible behavior but they are not required to implement the algorithm as presented.
[=Stratification=] is only defined when the following condition is satisfied. If a [=rule set=] does not meet this condition, then this specification does not define the outcome of [=rule set=] evaluation.
In other words, there is no `NOT` used in any rule that transitively depends on itself.
The following algorithm gives one possible stratification based solely on the rule set.
## output -- Map: Integer -> Set of rules.
define stratification(ruleSet):
let DP = Dependency graph for the rule set.
let stratumMap be a map from rule to integer
## The dependency graph should satisfy the stratification condition.
## Check unbounded work due to a violation of the stratification condition.
let limit = num rules + 1
let maxStratum = 0
## initialize stratumMap
foreach rule in ruleSet:
stratumMap.set(rule, 0)
endfor
boolean changed = true;
while changed:
changed = false;
foreach edge E in DP:
## Edge from pRule to qRule with a label
let pRule = source of edge
let qRule = destination of the edge
let label = edge label
if label == "positive" :
if stratumMap.get(pRule) < stratumMap.get(qRule) :
stratumMap.set(pRule, stratumMap.get(qRule))
changed = true;
endif
endif
if label == "negative" :
if stratumMap.get(pRule) <= stratumMap.get(qRule) :
let xStratum = 1 + stratumMap.get(qRule)
if ( xStratum > limit )
## Stratification requirement violated
error "Stratification error"
endif
stratumMap.put(pRule, xStratum)
maxStratum = max(maxStratum, xStratum)
changed = true;
endif
endif
endfor
endwhile
## Initialize the result map.
let stratumLevels be a map from integer to a set of rules
for i = 0 to maxStratum
stratumLevels.set(i, {})
endfor
## Gather rules in stratumMap with the same level number
for rule R in map stratumMap:
let stratumNum = stratumMap.get(R)
let S = {R} ∪︀ stratumLevel.get(stratumNum)
stratumLevel.set(stratumNum, S)
endfor
the result is stratumLevels
endfunction
A consequence of the [=stratification condition=] is that when a rule containing a [=negation element=] is evaluated, the data used to determine the outcome of that [=negation element=], whether in the [=base graph=] or the [=inference graph=], is fixed and will not change during evaluation.
There are two concrete syntaxes.
Shape Rules Language:
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 Rules syntax:
PREFIX : <http://example/>
PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX sh: <http://www.w3.org/ns/shacl#>
PREFIX srl: <http://www.w3.org/ns/shacl-rules#>
PREFIX sparql: <http://www.w3.org/ns/sparql#>
:ruleSet-1
rdf:type srl:RuleSet;
srl:data (
<<( :x :p 1 )>>
<<( :x :q 2 )>>
);
srl:ruleSet (
[
rdf:type srl:Rule;
srl:head (
[ srl:subject [ srl:var "x" ] ; srl:predicate :bothPositive; srl:object true ]
) ;
srl:body (
[ srl:subject [ srl:var "x" ]; srl:predicate :p; srl:object [ srl:var "v1" ] ]
[ srl:expr [ sparql:greaterThan ( [ srl:var "v1" ] 0 ) ] ]
[ srl:subject [ srl:var "x" ] ; srl:predicate :q; srl:object [ srl:var "v2" ] ]
[ srl:expr [ sparql:greaterThan ( [ srl:var "v2" ] 0 ) ] ]
);
]
[
rdf:type srl:Rule;
srl:head (
[ srl:subject [ srl:var "x" ] ; srl:predicate :oneIsZero ; srl:object true ]
) ;
srl:body (
[ srl:subject [ srl:var "x" ] ; srl:predicate :p ; srl:object [ srl:var "v1" ] ]
[ srl:subject [ srl:var "x" ] ; srl:predicate :q ; srl:object [ srl:var "v2" ] ]
[ srl:expr [ sparql:function-or (
[ sparql:equals ( [ srl:var "v1" ] 0 ) ]
[ sparql:equals ( [ srl:var "v2" ] 0 ) ]
) ]
]
);
]
) .
The grammar is given below.
Mapping the AST to the abstract syntax.
Additional helpers (short-hand abbreviations):
These allow for well-known rule patterns and also specialised implementations in basic engines.
TRANSITIVE(uri)SYMMETRIC(uri)INVERSE(uri)At risk:
`TRANSITIVE` has both implementation and concise expression advantages. Implementation advantages for `SYMMETRIC` and `INVERSE` are not clear.
Vocabulary: shacl-rules.ttl.
Well-formedness:
Describe how the abstract model maps to triples.
Process : accumulators, bottom up/ Walk the structure.
All triples not in the syntax are ignored. No other "srl:" predicates are allowed (??).
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, called the base graph, and a rule set RS. Output: an RDF graph GI of inferred triples
The inferred triples do not include triples present in the set of triples of the [=base graph=].
μ : 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 `graphMatch(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.
graphMatch(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(μ1, μ2) = { μ |
μ(v) = μ1(v) if v in dom(μ1)
μ(v) = μ2(v) otherwise }
merge(S1, S2) = { μ |
μ1 in S1, μ2 in S2
and compatible(μ1, μ2)
μ(v) = merge(μ1, μ2)
Say the domain is `dom(S1) ∪︀ dom(S2)`.
Say that two solutions that have no variables in common are compatible.
The first step in evaluating a [=rule set=] is to prepare a single, valid rule set. This involves gathering all imported rule sets, building a single, combined rule set, and then calculating the stratification for the combined rule set.
@@Walk imports - visit only once@@
@@TODO: consider defining a rule set as having two components and a separate set of imports. i.e. imports are in the parsing and sorted out to give a usable "rule set" of "rules + data"
A [=rule set=] has three components: R.rules, R.data and R.imports. The rule set merge of two rule sets RS1 and RS2 giving rule set MR:
MR.rules = RS1.rules ∪︀ RS2.rules
MR.data = merge(RS1.data, RS2.data)
MR.imports = {}
where `merge` is the RDF merge operation.
define imports(rule set RS, set of URLs V), returning rule set
let I = the set of import URLs declared for the rule set RS
let RS2 = RS
foreach URL x in I:
if x ∉ V:
V = V ∪︀ { x }
read rule set RS3 from URL x
RS2 = rulesetMerge(RS2, imports(RS3, V))
endif
endfor
result is RS2
enddefine
let RS be a ruleset
let V = {}
if RS has a location, V = { location of RS }
result is imports(RS, V)
Sketch
@@ Reference SPARQL expression evaluation Expression Evaluation @@ Reference SPARQL EBV Effective Boolean Value (EBV) define evalFunction(F, μ): let [x/μ] be if x is an RDF term, then [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. return evalFunction(F(expr1, expr2 ...), row) = F(evalFunction(expr1, row), evalFunction(expr2, row), ...) if an error is returned by evalFunction: return error return evalFunction(FF(expr1, expr2) , row) = ... things that are not functions like `IF`
let R be a well-formed rule.
let rule R = (H, B) where
H is the sequence of triple templates in the head
B is the sequence of triple pattern elements,
condition elements, negation elements,
and assignment elements in the body
# Solution sequence of one solution that does not map any variables.
let SEQ0: Solution sequence = { μ0 }
let G = evaluation graph
# Evaluate rule body
# This function returns a sequence of solutions
define evalRuleElements(B, SEQ, G):
for each rule element rElt in B:
if rElt is a triple pattern TP:
X = graphMatch(G, TP)
SEQ1 = {}
for each μ1 in X:
for each μ2 in SEQ:
if compatible(μ1, μ2)
μ3 = merge(μ1, μ2)
add μ3 to SEQ1
endif
endfor
endfor
if rElt is a condition element with expression F:
SEQ1 = {}
for each solution μ in SEQ:
let x = evalFunction(F, μ)
if x is true:
add μ to SEQ1
endif
endfor
endif
if rElt is a negation expression with body elements N:
SEQ1 = {}
for each solution μ in SEQ:
S = sequence{ μ }
NEG = evalRuleElements(N, S, G)
if NEG is empty
add μ to SEQ1
endif
endfor
endif
if rElt is an assignment with variable V and expression expr
SEQ1 = {}
for each solution S in SEQ:
let x = evalFunction(expr, S)
if x is not an error:
add(V, x) to S
add S to SEQ1
else
# Error: drop solution S
endif
endfor
endif
if SEQ1 is empty
SEQ = {}
return SEQ
endif
SEQ = SEQ1
endfor
return SEQ
enddefine
let SEQ = evalRuleElements(B, SEQ0, G)
# Evaluate rule head
let H = empty set
for each μ in SEQ:
let S = {}
for each triple template TT in head
let triple = subst(μ, TT)
Add triple to S
endfor
H = H union S
endfor
result eval(R, G) is H
Note that `H` may contain triples that are also in the data graph.
let G0 be the input base graph
let RS be the rule set
let D be the graph of all DATA triples in RS
Apply stratification to RS
let L be the sequence of layers after stratification
# Inference graph
let GI = { t ∈ D | t ∉ in G0 }
# Evaluation graph.
let GE = G0 ∪︀ D
for each layer in L:
let finished = false
while !finished:
finished = true
for each rule in layer:
let X = eval(rule, GE)
let Y = { t ∈ X | t ∉ in GE }
if Y is not empty:
finished = false
GI = Y ∪︀ GI
GE = Y ∪︀ GE
endif
endfor
endwhile
endfor
the result is GI
[1] |
RuleSet |
::= | RuleOrDataBlock |
[2] |
RuleOrDataBlock |
::= | Prologue ( RuleOrData+ ( Prologue1 RuleOrData? )* )? |
[3] |
RuleOrData |
::= | Rule | Data |
[4] |
Prologue |
::= | Prologue1* |
[5] |
Prologue1 |
::= | BaseDecl | PrefixDecl | VersionDecl | ImportsDecl |
[6] |
BaseDecl |
::= | 'BASE' IRIREF |
[7] |
PrefixDecl |
::= | 'PREFIX' PNAME_NS IRIREF |
[8] |
VersionDecl |
::= | 'VERSION' VersionSpecifier |
[9] |
VersionSpecifier |
::= | STRING_LITERAL1 | STRING_LITERAL2 |
[10] |
ImportsDecl |
::= | 'IMPORTS' iri |
[11] |
Rule |
::= | Rule1 | Rule2 | Declaration |
[12] |
Rule1 |
::= | 'RULE' HeadTemplate 'WHERE' BodyPattern |
[13] |
Rule2 |
::= | 'IF' BodyPattern 'THEN' HeadTemplate |
[14] |
Declaration |
::= | ( 'TRANSITIVE' '(' iri ')' | 'SYMMETRIC' '(' iri ')' | 'INVERSE' '(' iri ',' iri ')' ) |
[15] |
Data |
::= | 'DATA' '{' TriplesDataBlock? '}' |
[16] |
TriplesDataBlock |
::= | TriplesSameSubject ( '.' TriplesDataBlock? )? |
[17] |
HeadTemplate |
::= | '{' HeadTemplateBlock? '}' |
[18] |
BodyPattern |
::= | '{' BodyTriplesBlock? ( BodyNotTriples BodyTriplesBlock? )* '}' |
[19] |
BodyNotTriples |
::= | Filter | Negation | Assignment |
[20] |
BodyTriplesBlock |
::= | TriplesBlock |
[21] |
Negation |
::= | 'NOT' '{' BodyBasic '}' |
[22] |
BodyBasic |
::= | BodyTriplesBlock? ( Filter BodyTriplesBlock? )* |
[23] |
HeadTemplateBlock |
::= | TriplesBlock |
[24] |
TriplesBlock |
::= | TriplesSameSubjectPath ( '.' TriplesBlock? )? |
[25] |
ReifiedTripleBlock |
::= | ReifiedTriple PropertyList |
[26] |
ReifiedTripleBlockPath |
::= | ReifiedTriple PropertyListPath |
[27] |
Assignment |
::= | 'SET' '(' Var ':=' Expression ')' |
[28] |
Reifier |
::= | '~' VarOrReifierId? |
[29] |
VarOrReifierId |
::= | Var | iri | BlankNode |
[30] |
Filter |
::= | 'FILTER' Constraint |
[31] |
Constraint |
::= | BrackettedExpression | BuiltInCall | FunctionCall |
[32] |
FunctionCall |
::= | iri ArgList |
[33] |
ArgList |
::= | NIL | '(' Expression ( ',' Expression )* ')' |
[34] |
ExpressionList |
::= | NIL | '(' Expression ( ',' Expression )* ')' |
[35] |
TriplesSameSubject |
::= | VarOrTerm PropertyListNotEmpty | TriplesNode PropertyList | ReifiedTripleBlock |
[36] |
PropertyList |
::= | PropertyListNotEmpty? |
[37] |
PropertyListNotEmpty |
::= | Verb ObjectList ( ';' ( Verb ObjectList )? )* |
[38] |
Verb |
::= | VarOrIri | 'a' |
[39] |
ObjectList |
::= | Object ( ',' Object )* |
[40] |
Object |
::= | GraphNode Annotation |
[41] |
TriplesSameSubjectPath |
::= | VarOrTerm PropertyListPathNotEmpty | TriplesNodePath PropertyListPath | ReifiedTripleBlockPath |
[42] |
PropertyListPath |
::= | PropertyListPathNotEmpty? |
[43] |
PropertyListPathNotEmpty |
::= | ( VerbPath | VerbSimple ) ObjectListPath ( ';' ( ( VerbPath | VerbSimple ) ObjectListPath )? )* |
[44] |
VerbPath |
::= | Path |
[45] |
VerbSimple |
::= | Var |
[46] |
ObjectListPath |
::= | ObjectPath ( ',' ObjectPath )* |
[47] |
ObjectPath |
::= | GraphNodePath AnnotationPath |
[48] |
Path |
::= | PathSequence |
[49] |
PathSequence |
::= | PathEltOrInverse ( '/' PathEltOrInverse )* |
[50] |
PathEltOrInverse |
::= | PathElt | '^' PathElt |
[51] |
PathElt |
::= | PathPrimary |
[52] |
PathPrimary |
::= | iri | 'a' | '(' Path ')' |
[53] |
TriplesNode |
::= | Collection | BlankNodePropertyList |
[54] |
BlankNodePropertyList |
::= | '[' PropertyListNotEmpty ']' |
[55] |
TriplesNodePath |
::= | CollectionPath | BlankNodePropertyListPath |
[56] |
BlankNodePropertyListPath |
::= | '[' PropertyListPathNotEmpty ']' |
[57] |
Collection |
::= | '(' GraphNode+ ')' |
[58] |
CollectionPath |
::= | '(' GraphNodePath+ ')' |
[59] |
AnnotationPath |
::= | ( Reifier | AnnotationBlockPath )* |
[60] |
AnnotationBlockPath |
::= | '{|' PropertyListPathNotEmpty '|}' |
[61] |
Annotation |
::= | ( Reifier | AnnotationBlock )* |
[62] |
AnnotationBlock |
::= | '{|' PropertyListNotEmpty '|}' |
[63] |
GraphNode |
::= | VarOrTerm | TriplesNode | ReifiedTriple |
[64] |
GraphNodePath |
::= | VarOrTerm | TriplesNodePath | ReifiedTriple |
[65] |
VarOrTerm |
::= | Var | RDFTerm |
[66] |
RDFTerm |
::= | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | NIL | TripleTerm |
[67] |
ReifiedTriple |
::= | '<<' ReifiedTripleSubject Verb ReifiedTripleObject Reifier? '>>' |
[68] |
ReifiedTripleSubject |
::= | Var | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | ReifiedTriple |
[69] |
ReifiedTripleObject |
::= | Var | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | ReifiedTriple | TripleTerm |
[70] |
TripleTerm |
::= | '<<(' TripleTermSubject Verb TripleTermObject ')>>' |
[71] |
TripleTermSubject |
::= | Var | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode |
[72] |
TripleTermObject |
::= | Var | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | TripleTerm |
[73] |
TripleTermData |
::= | '<<(' TripleTermDataSubject ( iri | 'a' ) TripleTermDataObject ')>>' |
[74] |
TripleTermDataSubject |
::= | iri | RDFLiteral | NumericLiteral | BooleanLiteral |
[75] |
TripleTermDataObject |
::= | iri | RDFLiteral | NumericLiteral | BooleanLiteral | TripleTermData |
[76] |
VarOrIri |
::= | Var | iri |
[77] |
Var |
::= | VAR1 | VAR2 |
[78] |
Expression |
::= | ConditionalOrExpression |
[79] |
ConditionalOrExpression |
::= | ConditionalAndExpression ( '||' ConditionalAndExpression )* |
[80] |
ConditionalAndExpression |
::= | ValueLogical ( '&&' ValueLogical )* |
[81] |
ValueLogical |
::= | RelationalExpression |
[82] |
RelationalExpression |
::= | NumericExpression ( '=' NumericExpression | '!=' NumericExpression | '<' NumericExpression | '>' NumericExpression | '<=' NumericExpression | '>=' NumericExpression | 'IN' ExpressionList | 'NOT' 'IN' ExpressionList )? |
[83] |
NumericExpression |
::= | AdditiveExpression |
[84] |
AdditiveExpression |
::= | MultiplicativeExpression ( '+' MultiplicativeExpression | '-' MultiplicativeExpression | ( NumericLiteralPositive | NumericLiteralNegative ) ( ( '*' UnaryExpression ) | ( '/' UnaryExpression ) )* )* |
[85] |
MultiplicativeExpression |
::= | UnaryExpression ( '*' UnaryExpression | '/' UnaryExpression )* |
[86] |
UnaryExpression |
::= | '!' PrimaryExpression |
[87] |
PrimaryExpression |
::= | BrackettedExpression | BuiltInCall | iriOrFunction | RDFLiteral | NumericLiteral | BooleanLiteral | Var | ExprTripleTerm |
[88] |
ExprTripleTerm |
::= | '<<(' ExprTripleTermSubject Verb ExprTripleTermObject ')>>' |
[89] |
ExprTripleTermSubject |
::= | iri | RDFLiteral | NumericLiteral | BooleanLiteral | Var |
[90] |
ExprTripleTermObject |
::= | iri | RDFLiteral | NumericLiteral | BooleanLiteral | Var | ExprTripleTerm |
[91] |
BrackettedExpression |
::= | '(' Expression ')' |
[92] |
BuiltInCall |
::= | 'STR' '(' Expression ')' |
[93] |
iriOrFunction |
::= | iri ArgList? |
[94] |
RDFLiteral |
::= | String ( LANG_DIR | '^^' iri )? |
[95] |
NumericLiteral |
::= | NumericLiteralUnsigned | NumericLiteralPositive | NumericLiteralNegative |
[96] |
NumericLiteralUnsigned |
::= | INTEGER | DECIMAL | DOUBLE |
[97] |
NumericLiteralPositive |
::= | INTEGER_POSITIVE | DECIMAL_POSITIVE | DOUBLE_POSITIVE |
[98] |
NumericLiteralNegative |
::= | INTEGER_NEGATIVE | DECIMAL_NEGATIVE | DOUBLE_NEGATIVE |
[99] |
BooleanLiteral |
::= | 'true' | 'false' |
[100] |
String |
::= | STRING_LITERAL1 | STRING_LITERAL2 | STRING_LITERAL_LONG1 | STRING_LITERAL_LONG2 |
[101] |
iri |
::= | IRIREF | PrefixedName |
[102] |
PrefixedName |
::= | PNAME_LN | PNAME_NS |
[103] |
BlankNode |
::= | BLANK_NODE_LABEL | ANON |
Productions for terminals:
[104] |
IRIREF |
::= | '<' ([^<>"{}|^`\]-[#x00-#x20])* '>' |
[105] |
PNAME_NS |
::= | PN_PREFIX? ':' |
[106] |
PNAME_LN |
::= | PNAME_NS PN_LOCAL |
[107] |
BLANK_NODE_LABEL |
::= | '_:' ( PN_CHARS_U | [0-9] ) ((PN_CHARS|'.')* PN_CHARS)? |
[108] |
VAR1 |
::= | '?' VARNAME |
[109] |
VAR2 |
::= | '$' VARNAME |
[110] |
LANG_DIR |
::= | '@' [a-zA-Z]+ ('-' [a-zA-Z0-9]+)* ('--' [a-zA-Z]+)? |
[111] |
INTEGER |
::= | [0-9]+ |
[112] |
DECIMAL |
::= | [0-9]* '.' [0-9]+ |
[113] |
DOUBLE |
::= | ( ([0-9]+ ('.'[0-9]*)? ) | ( '.' ([0-9])+ ) ) [eE][+-]?[0-9]+ |
[114] |
INTEGER_POSITIVE |
::= | '+' INTEGER |
[115] |
DECIMAL_POSITIVE |
::= | '+' DECIMAL |
[116] |
DOUBLE_POSITIVE |
::= | '+' DOUBLE |
[117] |
INTEGER_NEGATIVE |
::= | '-' INTEGER |
[118] |
DECIMAL_NEGATIVE |
::= | '-' DECIMAL |
[119] |
DOUBLE_NEGATIVE |
::= | '-' DOUBLE |
[120] |
STRING_LITERAL1 |
::= | "'" ( ([^#x27#x5C#xA#xD]) | ECHAR )* "'" |
[121] |
STRING_LITERAL2 |
::= | '"' ( ([^#x22#x5C#xA#xD]) | ECHAR )* '"' |
[122] |
STRING_LITERAL_LONG1 |
::= | "'''" ( ( "'" | "''" )? ( [^'\] | ECHAR ) )* "'''" |
[123] |
STRING_LITERAL_LONG2 |
::= | '"""' ( ( '"' | '""' )? ( [^"\] | ECHAR ) )* '"""' |
[124] |
ECHAR |
::= | '\' [tbnrf\"'] |
[125] |
NIL |
::= | '(' WS* ')' |
[126] |
WS |
::= | #x20 | #x9 | #xD | #xA |
[127] |
ANON |
::= | '[' WS* ']' |
[128] |
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] |
[129] |
PN_CHARS_U |
::= | PN_CHARS_BASE | '_' |
[130] |
VARNAME |
::= | ( PN_CHARS_U | [0-9] ) ( PN_CHARS_U | [0-9] | #x00B7 | [#x0300-#x036F] | [#x203F-#x2040] )* |
[131] |
PN_CHARS |
::= | PN_CHARS_U | '-' | [0-9] | #x00B7 | [#x0300-#x036F] | [#x203F-#x2040] |
[132] |
PN_PREFIX |
::= | PN_CHARS_BASE ((PN_CHARS|'.')* PN_CHARS)? |
[133] |
PN_LOCAL |
::= | (PN_CHARS_U | ':' | [0-9] | PLX ) ((PN_CHARS | '.' | ':' | PLX)* (PN_CHARS | ':' | PLX) )? |
[134] |
PLX |
::= | PERCENT | PN_LOCAL_ESC |
[135] |
PERCENT |
::= | '%' HEX HEX |
[136] |
HEX |
::= | [0-9] | [A-F] | [a-f] |
[137] |
PN_LOCAL_ESC |
::= | '\' ( '_' | '~' | '.' | '-' | '!' | '$' | '&' | "'" | '(' | ')' | '*' | '+' | ',' | ';' | '=' | '/' | '?' | '#' | '@' | '%' ) |
@@see the Turtle registration for format
The Internet Media Type (formerly known as MIME Type) for @@ is "text/shape-rules".
The information that follows has been submitted to the Internet Engineering Steering Group (IESG) for review, approval, and registration with IANA.
TODO
TODO
TODO
Many people contributed to this document, including members of the RDF Data Shapes Working Group.