Copyright © 2025-2026 World Wide Web Consortium. W3C® liability, trademark and permissive document license rules apply.
本文档定义了 SHACL 规则。
SHACL,即形状约束语言,是一种用于描述 RDF 图结构的语言。 SHACL 可用于多种目的,例如验证、推理、 领域建模、生成本体以告知其他代理、构建用户 界面、生成代码以及集成数据。
SHACL 规则通过根据一组规则与一个基础数据图的组合生成新的 RDF 数据, 来提供推理能力。规则可以表示为 RDF, 或表示为形状规则语言(SRL)。
本规范由 数据形状工作组发布。
本节描述本文档在发布时的状态。 当前 W3C 出版物列表以及本技术报告的最新修订版本可在 W3C 标准与草案 索引中找到。
本文档由 数据形状工作 组作为 工作草案发布,并使用 推荐标准 轨道。
作为 工作草案发布并不意味着 W3C 及其成员的认可。
这是一份草案文档,可能会随时被其他文档 更新、取代或废弃。除作为 进行中的工作之外,不宜引用本文档。 对这一即将成为推荐标准的文档的未来更新可能会纳入 新特性。
本文档由一个依据 W3C 专利 政策运作的工作组 生成。 W3C 维护一份 与该组交付成果相关的任何专利 披露的公开列表; 该页面还包括 披露专利的说明。实际 知晓某项专利且认为该专利包含 必要权利要求 的个人,必须根据 W3C 专利政策第 6 节披露该信息。
本文档受 2025 年 8 月 18 日 W3C 流程文档管辖。
本规范是 SHACL 1.2 规范系列的一部分。有关这些规范的更详细 介绍,请参见 SHACL 1.2 概述。
这些规范如下:
工作草案:
工作组说明草案:
实现者可以通过成功通过 SHACL 1.2 测试套件 的测试用例,部分检查其与上述规范的一致性级别。 但是请注意,通过测试套件中的所有测试并不意味着完全符合这些 规范。 它仅意味着该实现符合测试套件所测试的方面。
本文档介绍 SHACL 1.2 的推理规则,这是一种通过声明式规则 从现有 RDF 数据中派生新的 RDF 三元组的机制。 本文档定义基于规则的推理的语法和语义。
SHACL 规则的实现提供两种操作。 infer 操作将规则应用于给定的 基础图,并生成一个包含通过 执行规则派生出的 RDF 三元组的推理图。 是否将推理图与基础图合并是可选的,并 留给用户决定。query 操作用于确定某个 给定目标模式是否可以使用这些规则从基础图派生出来。
SHACL 规则允许使用新的 RDF 术语,包括空白节点, 这些术语可用于规则头部的三元组模板。
SHACL 规则还支持一些构造,例如失败即否定, 这些构造可能会因规则执行顺序不同而导致不同的推理图。 为避免这种情况,规则使用 分层技术进行求值,该技术在规则之间建立单一的隐式顺序, 确保始终生成相同的推理图。
以下其他规范提供了本文档中使用的基础术语:
本文档中的一些示例使用 RDF 1.2 Turtle [RDF12-TURTLE]。
在本文档中,使用以下命名空间前缀绑定:
| 前缀 | 命名空间 |
|---|---|
rdf: |
http://www.w3.org/1999/02/22-rdf-syntax-ns# |
rdfs: |
http://www.w3.org/2000/01/rdf-schema# |
srl: |
http://www.w3.org/ns/shacl-rules# |
xsd: |
http://www.w3.org/2001/XMLSchema# |
sparql: |
http://www.w3.org/ns/sparql# |
ex: |
http://example/ |
在整个文档中,将出现包含 Turtle 格式 RDF 图的彩色编码框。 这些 Turtle 文档片段使用上面给出的前缀绑定。
# This box represents rules
# This box represents input data
# This box represents inferred data
除标记为非规范性的章节外,本规范中的所有创作指南、图示、示例和注释也都是非规范性的。本规范中的其他所有内容均为规范性的。
本文档中的关键词 可以、必须、不得 和 应该 应按照 BCP 14 [RFC2119] [RFC8174] 中所述的方式解释,并且仅当它们像这里所示以全 大写形式出现时才如此。
本规范为以下内容定义了一致性标准:
一个符合要求的 SRL 文档是一个
RDF
字符串,它
符合从
RuleSet
产生式开始的语法,该产生式定义于
7. 形状规则语言
语法中。
本规范未定义 SPARQL-RL 处理器如何处理不符合要求的规则集。
本节为非规范性内容。
给定一个基础图和一个规则集,SHACL 规则会推断新的三元组。 求值的输出是一个推理图,其中包含派生出的、 不出现在基础图中的三元组。
每个规则都有一个称为主体的模式,以及一个 称为头部的结果模板。 规则通过查找主体中变量的值来执行, 使主体匹配组合后的基础图以及 到目前为止执行过程中产生的任何推理三元组。 然后使用这些值来实例化规则头部中的 三元组模板,以生成新的推理三元组。
规则会一直执行,直到不再推断出更多三元组; 随着新的推理三元组可用,规则可能会被执行多次。
SHACL 规则执行被定义为:在创建新的 RDF 术语时, 包括新的空白节点,以及在测试某个模式是否不存在时, 规则执行顺序不会导致不同结果。换言之,无论规则执行顺序如何, 都会生成相同的推理图。
SPARQL-RL 采用一种受 SPARQL 1.2 查询语言启发的易于人类理解的语法。 规则集求值包含与 SPARQL 类似的元素,但在细节上有所不同, 以确保无论规则执行顺序如何,都生成相同的推理图。
本节中的示例描述了软件组件及其 依赖关系:前端调用应用服务器,而 应用服务器查询一个存在已知 漏洞的数据库。
在第一个示例中,我们有以下数据图和规则集:
将上述规则应用于数据后,可以得出:
:frontend 依赖于 :app,而 :app 依赖于
:db,无论依赖关系属于何种类型。
然后,我们可以添加一条依赖于其他规则所生成的 :dependsOn 三元组的规则,
以推导出 :exposedTo 关系(另请参阅下面的 4.3 规则依赖关系):
上一节中的 :exposedTo 规则只能覆盖直接
依赖关系::frontend 并不直接依赖于 :db,因此
不会为 :frontend 推断出 :exposedTo 三元组。
为了沿任意长度的依赖链传播暴露关系,我们
将该规则替换为两条规则:组件会暴露于其自身具有的
漏洞,并且组件会暴露于其直接依赖项所暴露的任何
漏洞:
与上一个示例相比,这里新增了
:db :exposedTo :vuln1——数据库暴露于其
自身的漏洞——以及 :frontend :exposedTo :vuln1:
暴露关系会沿依赖链传播至 :frontend,
无论该链有多长。
最后一条规则是一条递归规则:规则体 依赖于规则头。
我们可以在规则体中使用表达式,以限制规则体匹配过程中 变量的取值。 例如,给定每个漏洞的严重程度, 我们可以为暴露于严重 漏洞的组件赋予状态:
FILTER 对表达式求值,如果表达式求值为 true,则保留当前
变量绑定集;如果表达式求值为 false,则丢弃
当前变量绑定集。
这与
SPARQL 的 FILTER 操作
相同,并且 SHACL Rules 提供了许多与 SPARQL 相同的函数和运算符。
否定允许你指定一个不得匹配的模式。 这称为“失败即否定”。
为了对否定元素求值,规则求值算法 会确保所有可能生成与否定元素中的模式相匹配的三元组的规则 均已执行完毕。这称为 分层,并确保否定基于 所有相关的可能三元组,无论这些三元组来自数据,还是 否定元素中的三元组均已执行完毕。包含该否定的规则被称为_依赖_于 生成这些三元组的规则(请参阅下面的 4.3 规则依赖关系)。
赋值允许你在规则体中将表达式的结果赋给变量。 这可用于根据数据创建新的 RDF 项。
空白节点可以在规则头中使用,并且每当规则求值生成三元组时, 每个空白节点都会生成一个新的空白节点。
涉及赋值的规则,以及在其 规则头中创建空白节点的规则,都是仅运行一次的规则。 此类规则会在所有可能生成其所依赖数据的规则之后运行, 并在任何依赖其所生成数据的规则之前运行。 在规则头中包含空白节点的规则也会创建新的 RDF 项,因此也是仅运行一次的规则。
此条件可确保此类规则不会循环回自身并导致生成数量不受限制的 RDF 项。
SHACL 规则集可以通过 在该规则集的规则集导入项中包含其他规则集的 URL 来引入它们。 这使得规则可以组织成在规则集之间共享的库。
在对规则集中的任何规则进行求值之前,会先处理该规则集的 IMPORTS 语句。
在导入步骤期间,如果被导入的规则集
有其自己的导入项,则也会递归处理这些导入项。
在处理规则集期间遍历 IMPORTS 语句可能会导致
循环导入。一个规则集只会被导入一次;
导入语句图中的环不会导致无限循环。
SRL 处理器可以选择是否支持导入规则集。 有关更多详细信息,请参阅4.5 处理 导入。
数据块允许直接向规则集求值简洁地提供 RDF 三元组。 数据块中的三元组会添加到推理图中,并可供 规则体进行匹配。
例如,规则集可以直接附带已断言的已知事实, 而不是从数据中推导这些事实:
数据块等同于一条规则体为空的规则: 无需对任何规则求值,其三元组便会成为推理图的 一部分。
规则被组织到规则集中。 规则集和数据图(即基础图)是求值的输入。 输出是一个称为推理图的图, 它是未出现在数据图中的三元组集合。
在求值期间,基于一条规则推理出的三元组 可供其他规则进行匹配。 规则集求值会持续进行,直至推理图 包含根据规则集和数据图的输入 可产生的所有三元组。
求值首先通过两个步骤 准备规则集, 然后再对规则本身进行求值:
规则集准备完毕后,求值会 按顺序取出分层中的每一层, 对该层中的规则进行求值直至完成, 然后再转到下一层。
基础数据是基础图中的三元组。 在规则集求值期间,可能 有必要仅对原始三元组进行模式匹配,而不对任何推断出的 三元组进行模式匹配。
设置默认值就是一个例子。 规则必须测试基础图是否已经包含一个值,如果不包含, 则规则可以计算一个默认值。
使用 NOT DATA 可允许规则主体的其他部分匹配推断出的三元组。
也可以通过使用 WHERE DATA,针对基础图执行所有规则主体匹配。
此类规则随后仅依赖于
基础图中的匹配。
形状规则抽象语法是 SHACL 规则的逻辑结构。 它用于定义 SHACL 规则的执行算法。
查询是确定给定的目标 模式能否使用规则集从一个基础图推导出的操作。 它可以不对所有规则进行求值;相反,它可以只对 回答查询目标所必需的规则进行求值。
在三元组模式或三元组 模板中, 元组的位置 1 非正式地称为主语, 位置 2 非正式地称为谓词,而 位置 3 非正式地称为宾语。
一系列规则元素中的元素从 1 开始编号。
以下表示法用于规则集和规则的各种组成部分。
| 组成部分 | 用法 |
|---|---|
ruleset.rules |
一个规则集的规则。 |
ruleset.data |
由规则集中的数据块并集 形成的 RDF 图。 |
ruleset.imports |
一个规则集的导入项集合。 |
rule.head |
规则头的三元组模板。 |
rule.body |
规则的规则元素。 |
rule.data |
一个布尔标志;如果为 false,则规则主体会 针对基础图进行匹配。 |
rule.id |
规则的标识符,它是一个 空白节点或 IRI。 |
filter.expr |
一个过滤 元素的表达式 |
assign.var |
[赋值 元素]的赋值变量。 |
assign.expr |
赋值表达式 |
negation.inner |
否定元素主体 是一个否定元素。 |
negation.data |
一个布尔标志;如果为 false,则 negation.inner 会
针对基础图进行匹配。 |
良构性是针对 规则集抽象语法的一组条件。这些条件共同确保 规则变量在 规则的头部中具有在规则主体中定义的值; 过滤器元素 或赋值表达式中的每个变量 在求值时都具有值; 并且规则中的每个 赋值都会引入一个新变量, 即一个此前未在规则主体中使用过的变量。
给定一个初始变量集合,我们为一系列规则元素 定义良构性。
令 elti 为一系列规则元素中的第 i 个元素。
令 varsi 为由 elti 定义的变量集合,其中:
令 V0 为一个序列的初始变量。
令 Vi 为 V0 与所有 varsj 的并集, 其中 j 小于 i。
令 Vall 为 VN, 其中 N 是该序列的长度。
给定一个变量集合 V0,如果满足以下条件,则 良构 序列是一系列规则元素:
如果在 V0 为空集的情况下,规则的规则主体序列是一个良构序列, 并且规则头的每个三元组模板中的每个变量 都是 Vall 的元素, 则该规则是一个良构 规则。
如果第二个规则的输出会影响第一个规则主体的求值,则规则 R1 依赖于规则 R2。
也就是说,R2 的头中有一个三元组模板,它可能生成一个与
R1 主体中的三元组模式匹配的三元组,
该三元组模式可以作为三元组模式元素,也可以位于否定元素内部。
有两种依赖关系:
封闭依赖和开放
依赖。
封闭依赖确保规则 R2
在规则 R1 执行之前已经生成其所有可能的输出。
如果规则依赖不是封闭的,则它是开放依赖,
这允许第一个规则 R1 在
规则 R2 可能再次运行以生成更多三元组时执行,
随后这些三元组可使 R1 使用来自 R2 的新三元组重新求值。
在第一个示例中,第一个规则对第二个规则具有开放依赖 关系。
在第二个示例中,第一个规则对第二个规则具有封闭依赖
关系:三元组模式 ?x :status :criticallyExposed
出现在第一个规则的一个否定元素内部,并且
与第二个规则头中的三元组模板匹配。
在第二个规则生成其所有可能的输出之前,
无法对第一个规则进行求值。
如果 R1 主体中的任一
三元组模式,无论是作为三元组模式元素还是
位于否定元素内部,
依赖
于
R2 头中的一个三元组模板,则规则 R1 依赖于 R2。
如果规则 R1 对规则
R2 的规则依赖不是
封闭依赖,则它是一个开放依赖。
也就是说,R1 中依赖于
R2 的任何三元组模式都仅作为三元组模式元素出现。
如果存在三元组模板中变量的值,使得在模板中用这些值替换变量后
得到一个三元组 T2,并且
T2 等于 T1,则一个三元组模板可以生成一个
RDF 三元组 T1。
类似地,如果存在三元组模式中变量的值,使得在模式中用这些值替换变量后
得到一个三元组 T2,并且
T2 等于 T1,则一个三元组模式匹配一个三元组
T1。
对于三元组模式,这包括
对称三元组。
如果一个变量在一个三元组 模板 或三元组模式中使用多次,则使用相同的 RDF 项 作为替换值。
用 RDF 项替换变量时,也包括 三元组项内部的变量。
规则之间的依赖关系表示为一个有向图, 称为依赖图。 该图的顶点是规则集中的规则,而边 根据依赖关系是开放依赖还是封闭依赖, 分别标记为开放或封闭。
R1 依赖于 R2,则存在一条从规则
R1
指向规则 R2 的边。
根据该依赖关系
是开放依赖还是封闭依赖,该边被标记为开放
或封闭。
R1 到 R2 的路径,
则规则 R1 对规则
R2 具有传递依赖。
依赖图不受数据图影响。
以下算法给出了一种从 规则集构造依赖 图的 可行方法。一致性取决于生成符合依赖图定义的依赖图, 而不取决于是否使用此过程。
define mergeLabel(oldLabel, newLabel):
## 封闭依赖优先于开放依赖。
if oldLabel == "open" and newLabel == "open":
return "open"
else:
return "closed"
endif
enddefine
## 输出 -- 具有规则顶点和带标签边的依赖图。
define buildDependencyGraph(ruleSet):
## edgeLabelMap 将 (R1, R2) 映射到 "open" 或 "closed"
let edgeLabelMap be a map from pair (rule, rule) to label
foreach rule R1 in ruleSet.rules:
## 将规则中的每个三元组模式 TP 分类为需要 "open" 或 "closed"
## 具体取决于它是否位于否定元素中。
let bodyDependencies = {}
foreach rule element RBE in R1.body:
if RBE is a negation element:
foreach triple pattern TP in RBE.inner
let item be a pair (TP, "closed")
add item to bodyDependencies
endfor
else if RBE is a triple pattern element of triple pattern TP:
let item be a pair (TP, "open")
add item to bodyDependencies
else if RBE is a condition element:
## 不执行任何操作
else if RBE is an assignment element:
## 不执行任何操作
endif
endfor
foreach pair (triple pattern TP, depLabel) in bodyDependencies:
if R1.body has an assignment element:
set depLabel to "closed"
endif
if R1.head has a triple template with a blank node:
set depLabel to "closed"
endif
## 查找此三元组模式元素或否定元素的依赖关系。
foreach rule R2 in ruleSet.rules:
foreach triple template TT in R2.head:
if triple pattern TP matches triple template TT:
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:
add an edge (R1 -> R2) labeled with label to DP
endfor
the result is DP
enddefine
分层是将一个规则集 划分为一系列有序分层层级(也称为 “层”,单数为“层”)的过程。较低层中的规则 先于较高层中的规则进行求值。
分层对规则之间的依赖关系施加约束, 以确保否定元素、赋值 元素以及 在规则头中创建的空白节点仅依赖于使用 较早(较低)的层和基础图计算出的结果。 这保证了在给定基础图上对规则集进行求值时,会得到唯一、明确定义且有限的 结果。
分层过程也可以用于作出其他求值 决策。本文档描述了一致 求值的必要条件,并给出了一种形成 分层的可行方式。实现需要满足 此处所述的条件,以获得兼容的行为,但并不 要求按照所给出的方式实现该算法。
一个分层层级 SL 是一对
互不相交的规则集合
(SL.once、SL.general)。
SL.once 包含一次性运行规则,即
使用赋值元素或
在规则头中生成
空白节点的规则;这些规则中的每一个都在分层层级开始求值时恰好求值
一次。
SL.general 包含其余规则,这些规则会被反复求值,
直到不再推断出新的三元组。
仅当满足以下 条件时,才定义分层。如果一个规则集不满足此条件,则本 规范不会为此类规则集的求值定义结果。
以下算法给出了一种仅基于 规则集的可行分层方法。
## 输出 -- 映射:整数 -> 规则集合。
define stratification(ruleSet):
let DP = Dependency graph for the rule set.
let stratumMap be a map from rule to integer
## 依赖图应满足分层条件。
## 对无界分层的检查是一项保护措施
## 用于处理违反分层条件的情况。
let limit = num rules + 1
let maxStratum = 0
## 初始化 stratumMap
foreach rule in ruleSet.rules:
stratumMap.set(rule, 0)
endfor
boolean changed = true;
while changed:
changed = false;
foreach edge E in DP:
## 从 pRule 到 qRule 的带标签边
let pRule = source of edge
let qRule = destination of the edge
let label = edge label
if label == "open" :
if stratumMap.get(pRule) < stratumMap.get(qRule) :
stratumMap.set(pRule, stratumMap.get(qRule))
changed = true;
endif
endif
if label == "closed" :
if stratumMap.get(pRule) <= stratumMap.get(qRule) :
let xStratum = 1 + stratumMap.get(qRule)
if ( xStratum > limit )
## 违反分层要求
error "Stratification error"
endif
stratumMap.set(pRule, xStratum)
maxStratum = max(maxStratum, xStratum)
changed = true;
endif
endif
endfor
endwhile
## 初始化结果映射。
let stratumRules be a map from integer to rules.
for i = 0 to maxStratum
stratumRules.set(i, {})
endfor
## 收集 stratumMap 中具有相同层级编号的规则
for rule R in map stratumMap:
let stratumNum = stratumMap.get(R)
add R to stratumRules.get(stratumNum)
endfor
## 将每个层级划分为 once 和 general
let stratumLevels be a sequence of pairs of sets of rules.
for i = 0 to maxStratum:
let rules = stratumRules.get(i)
let once = { R in rules | R is a run-once rule }
let general = rules \ once
stratumLevels.set(i, pair(once, general))
endfor
the result is stratumLevels
enddefine
对完整规则集逐步应用依赖关系分析和 分层,并继续进行 求值的过程,可参见 6.6 完整 示例。
从 Web 读取文档具有安全方面的影响。 SRL 处理器可以选择是否支持导入规则集。 此外,实现可以提供部分支持, 例如支持导入某些规则集而不支持其他规则集, 并且可能从经过验证的副本导入。
以下条件适用于导入处理:
IMPORTS 语句的规则集,并发出错误信号。
IMPORTS 语句时,为避免无限循环,
处理器必须仅导入一个规则集一次。
已解析规则集通过递归读取另一个规则集的 导入项中提到的所有规则集, 从该规则集生成。
示例算法见A. 示例导入算法。
本节为非规范性内容。
SRL 与 SPARQL 关系密切。SRL 旨在与 SPARQL 兼容, 而且 SRL 中的许多构造都取自 SPARQL 或受其启发。 但是,两者之间也存在一些差异。
在 SRL 中,RULE 变量在使用前始终会被绑定,无论是用于
FILTER 和 SET 的表达式,还是用于三元组模板
(位于规则头部中)。
如果变量出现在 CONSTRUCT 模板中,但在 WHERE 子句中未被赋值,
SPARQL CONSTRUCT 查询将产生部分结果。
SET 形式与 SPARQL BIND 形式具有不同的错误
处理行为。在 SET 中遇到错误会导致
当前解被过滤掉,而 BIND 不会
在当前解中设置该变量,但会继续传递该解。
SET(?var := expr) 等同于
SPARQL 中的
BIND(expr AS ?var),后跟
FILTER(BOUND(?var))。
WHERE 子句的语法
不包含 UNION 或 OPTIONAL 语法。
这些 SPARQL 元素可能导致未绑定变量。
UNION 或 OPTIONAL 的效果可通过使用
良构规则来实现,从而可以分析规则集。
其他差异包括:
NOT 的语法将内部主体限制为三元组
模式和过滤器,并且不允许嵌套模式,
这与 SPARQL FILTER NOT EXISTS 不同。
* 和 +。
任意长度路径可在 SRL 中
使用递归来表达,这是一种更通用的方法。
COALESCE,也没有 BOUND。没有哈希函数。也
没有 RAND,因为它每次被
调用时都会产生不同的结果。
NOW(),并将其定义为在整个规则集求值过程中返回相同的
时间点。这与
SPARQL 中的 NOW() 相同。
本节定义在给定数据上对规则集求值的结果。 它并不规定该算法作为实现方法。 实现可以使用任何能生成相同结果的算法。
输入: 数据图 G,称为基础图,以及规则集 RS。
输出: 由推理三元组组成的 RDF 图 GI
推理出的三元组不包括 基础图的三元组集合中已有的三元组。
μ : V → T,
其中 V 是所有变量的集合,
而 T 是所有 RDF
项的集合。
μ 的定义域表示
为 dom(μ),它是
V 中 μ 有定义的子集。当明确指的是解映射时,我们使用术语
解。
用 μ0 表示满足以下条件的解映射:
dom(μ0) 是空集。
subst(μ, 三元组模式)
,它返回一个三元组模式
,其中三元组模式中每次出现的、位于
dom(μ)
中的变量
都会被RDF 项替换,该 RDF 项由
解映射针对 var 给出。
如果得到的三元组模式不包含变量,那么它就是一个 RDF 三元组。
设 G 为一个 RDF 图,TP 为一个三元组模式。
函数 graphMatch(G, TP) 返回所有可能的
解的集合,这些解
应用于三元组模式时,会产生一个位于
求值图中的三元组
设 S1 和 S2 为解。
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) }
merge(S1, S2) 的定义域是 domain(S1) ∪︀ domain(S2)
如果两个解没有共同变量,则它们是兼容的,
并且两个解的合并是 S1 和
S2 的并集。
EBV(x) 返回一个
RDF 项的有效布尔值。
规则集的求值涉及收集所有导入的规则集, 并按照 4.5 处理 导入中的描述构建单个合并规则集。
然后按照以下步骤准备合并后的规则集 以进行求值。
表达式无论是用于过滤元素还是 赋值元素,都会相对于一个 解映射求值;该解映射为表达式中的每个变量提供一个 RDF 术语值。 4.2 良构性条件 中的良构性要求确保表达式中的所有变量 都出现在解映射中。
define evalFunction(F, μ):
## F is an expression: an RDF term, a variable, or op(expr1, ..., exprN)
## where op is a function or a functional form.
if F is an RDF term:
return F
if F is a variable:
## By well-formedness, F ∈ dom(μ).
return μ(F)
## F is of the form F= op(expr1, ..., exprN)
if op is a functional form (e.g. IF, logical-or):
## Evaluated specifically for op; op may evaluate only some arguments.
## For example, IF(c, t, f) evaluates c, then exactly one of t or f.
return the value defined for op over expr1, ..., exprN under μ
## op is an ordinary function: evaluate all arguments first.
return F(evalFunction(expr1, row), ..., evalFunction(exprN, row))
enddefine
一个规则通过从规则主体计算一个解 序列来求值, 然后使用该解序列中的每个解映射, 使用规则头生成三元组。
三元组模式中的空白节点表现得像变量。 这与 SPARQL 图模式匹配兼容。
# Evaluate rule body
# This function returns a sequence of solutions
define evalRuleElements(B, SEQ, G, GD):
where
B is a sequence of rule elements
SEQ is a solution sequence
G and GD are RDF graphs
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
endif
if rElt is a condition element F:
SEQ1 = {}
for each solution μ in SEQ:
let x = evalFunction(F.expr, μ)
if EBV(x) is true:
add μ to SEQ1
endif
endfor
endif
if rElt is a negation expression N:
SEQ1 = {}
for each solution μ in SEQ:
S = sequence{ μ }
if rElt with DATA:
NEG = evalRuleElements(N.inner, S, GD, GD)
else:
NEG = evalRuleElements(N.inner, S, G, GD)
if NEG is empty
add μ to SEQ1
endif
endfor
endif
if rElt is an assignment A:
SEQ1 = {}
for each solution μ in SEQ:
let x = evalFunction(A.expr, μ)
if x is not an error:
## Add mapping V -> x to solution μ
let μ2 be a solution mapping μ ∪︀ { (A.var, x) }
add μ2 to SEQ1
else
# Error: drop solution μ
endif
endfor
endif
SEQ = SEQ1
endfor
return SEQ
enddefine
define evalRule(R, G, GD):
where
R is a well-formed rule
G and GD are RDF graphs
let B be R.body
where each blank node in a triple pattern in R.body
is replaced by a variable which is not used in the rule.
The same variable is used for each occurrence of the
same blank node, and a different variable is used for
each different blank node.
# Solution sequence of one solution that does not map any variables.
let SEQ0: Solution sequence = { μ0 }
if R.data:
let SEQ = evalRuleElements(B, SEQ0, GD, GD)
else
let SEQ = evalRuleElements(B, SEQ0, G, GD)
# Evaluate rule head
let OUT = empty set
for each μ in SEQ:
let S = {}
for each triple template TT in R.head:
let triple = subst(μ, TT)
Add triple to S
endfor
OUT = OUT union S
endfor
return OUT
enddefine
OUT 可能包含同样存在于数据图中的三元组。
规则集的求值被定义为执行该规则集的 分层中的每个层,其中每一层都按顺序 完整执行,然后再转到下一个层。 对一个层进行求值时,首先对 该层中的每条单次运行规则进行求值,然后重复对 该层中的一般规则进行求值,直至不再产生新的三元组。
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 LS be the sequence of layers after stratification
# Inference graph
let GI = { t ∈ D | t ∉ G0 }
let GD = G0 ∪︀ D
# Evaluation graph.
let GE = GD
for each stratum ST in LS:
for each rule R in ST.once:
let X = evalRule(R, GE, G0)
let Y = { t ∈ X | t ∉ GE }
GI = GI ∪︀ Y
GE = GE ∪︀ Y
endfor
let finished = false
while !finished:
finished = true
for each rule R in ST.general:
let X = evalRule(R, GE, G0)
let Y = { t ∈ X | t ∉ GE }
if Y is not empty:
finished = false
GI = GI ∪︀ Y
GE = GE ∪︀ Y
endif
endfor
endwhile
endfor
the result is GI
本节为非规范性内容。
本节逐步说明完整规则集的求值过程:确定规则依赖关系 (4.3 规则 依赖关系)、 计算分层 (4.4 分层), 并依次对每一层进行求值以产生 推理图 (6.5 规则集 求值)。
该示例描述了软件组件及其依赖关系:
前端依赖应用服务器,而应用服务器依赖
数据库和日志记录库。数据库存在一个已知的
漏洞。规则沿依赖链传播漏洞暴露情况,
为暴露于严重漏洞的组件赋予 :criticallyExposed 状态,为
不严重的组件赋予 :safeToDeploy 状态,并为每个
严重暴露的组件创建一条通知。
(在实践中,漏洞是否影响某个组件
取决于所部署的版本;该示例省略了版本,并
直接声明了漏洞。)
为简洁起见,基础图直接声明 :dependsOn 三元组,
而不是像
3.1 基本模式中那样从更具体的关系推导它们。
这些规则在注释中标记为 R1 到 R5,并在
本节其余部分中通过这些标签引用。
它们是在
3.2 递归至
3.5 赋值
和创建 RDF 项中引入的规则。
该规则集没有 IMPORTS,因此导入处理
不会改变它;也没有 DATA 块,因此求值仅
从基础图开始。
将每个规则体中的每个三元组模式与 每个规则头中的三元组模板进行比较,以确定哪些规则 依赖于哪些规则 (4.3 规则 依赖关系)。
R1 的规则体是单个三元组模式
?x :hasVulnerability ?v。没有任何规则头包含
能够生成谓词为 :hasVulnerability 的三元组的三元组模板,
因此该模式只能与数据匹配。
R1 没有依赖关系。
R2 的规则体包含两个三元组模式。
?x :dependsOn ?y 无法与任何规则头匹配,因此它只能
与数据匹配。?y :exposedTo ?v 与 R1 的规则头模板
?x :exposedTo ?v 相匹配,也与 R2
自身的规则头相匹配。两处出现的模式都是三元组模式元素
(它们未出现在否定元素内,并且
R2
没有赋值元素,其
规则头中也没有空白节点),因此
R2 对 R1 具有一个开放依赖关系,并且
对自身也具有一个开放依赖关系。
R2 对自身的依赖关系使 R2 成为一条递归规则。
R3 的规则体中,模式 ?x :exposedTo ?v 与
R1 和 R2 的规则头相匹配,使 R3 分别对二者具有一个开放
依赖关系。模式 ?v :severity ?s 不与任何
规则头匹配。
R4 的规则体中,模式 ?x rdf:type :Component
不与任何规则头匹配:唯一一个谓词为
rdf:type 的规则头模板位于 R5 中,而其对象 :Notification
与 :Component 是不同的 RDF 项,因此生成的
三元组均无法匹配。
模式 ?x :status :criticallyExposed 出现在
否定元素
NOT { ?x :status :criticallyExposed } 内,并且
与 R3 的规则头模板相匹配。由于该模式
出现在否定元素中,因此 R4 对 R3 具有一个封闭
依赖关系。
R5 的规则体中,模式 ?x :status :criticallyExposed
与 R3 的规则头相匹配。R5 的规则头包含一个
空白节点,因此它对 R3 的依赖关系是一个封闭
依赖关系,即使该模式是普通的
三元组模式元素。
所有规则体中的模式均不与
R5 的规则头模板匹配,因此没有规则依赖于 R5。
因此,依赖图包含五个顶点和六条 边:
R2 → R1(开放)
R2 → R2(开放)
R3 → R1(开放)
R3 → R2(开放)
R4 → R3(封闭)
R5 → R3(封闭)
尽管 R4 和 R5 均不直接依赖于 R1 或
R2,但二者都通过
R3 对它们具有传递依赖关系。
图中唯一的环是自环边 R2 → R2,
它是开放依赖关系。没有任何环涉及封闭
依赖关系,因此满足分层条件,
并且该规则集具有明确定义的结果。
按照 分层算法, 每条规则最初都位于第 0 层。随后反复检查 各条边,直到不再发生变化:
R4 → R3
(封闭)发现 R4 和 R3 均位于第 0 层,因此将 R4
移至第 1 层。同样,边 R5 → R3
(封闭)将 R5 移至第 1 层。
再次遍历各条边不会产生任何变化,因此各层
已最终确定。随后将每一层划分为
仅运行一次的规则和一般规则。
R5 的规则头中有一个空白
节点,因此它是一条仅运行一次的规则;其他规则
均没有赋值元素,其规则头中也没有空白节点。
分层结果为:
R1、R2、R3;没有仅运行一次的规则R5;一般规则 R4
此顺序体现了较高
层中规则的意图。只有在
推导出所有 :status :criticallyExposed 三元组之后,才能确定组件是否可以安全部署,
以及哪些组件需要通知——而推导这些三元组的规则
R3 会在下一层完成,同时与向其提供数据的
规则 R1 和 R2 一同完成。
求值遵循
6.5 规则集
求值算法。
求值图 GE 初始为基础图,而
推理图 GI 初始为空。按顺序对每一层求值直至
完成:分轮对该层的规则求值,
并重复各轮,直到某一轮不再生成新的三元组。
该算法未规定一轮中同一层规则的
求值顺序;此处的执行轨迹采用
R1、R2、R3 的顺序。不同顺序可能会将相同的
推理结果分散到不同数量的轮次中,但由于
第 0 层会运行至完成,因此最终生成的图相同。
由于此规则集没有 DATA 块,因此在求值过程中的每个时刻,
GE 都等于 G0 加上
GI。因此,执行轨迹仅跟踪 GI,显示求值进行过程中
它的状态,并用生成每个新三元组的规则进行标记。
第 0 层,第一轮。
R1:规则体与
:db :hasVulnerability :vuln1 匹配,将
?x = :db 和 ?v = :vuln1 绑定。
规则头生成 :db :exposedTo :vuln1。
该三元组不在 GE 中,因此将其添加到 GE 和 GI。
R2:规则体将 ?x :dependsOn ?y 与
?y :exposedTo ?v 连接。此时 GE 包含
:db :exposedTo :vuln1,因此连接产生一个解:
?x = :app、?y = :db 和 ?v = :vuln1
(来自 :app :dependsOn :db)。
规则头生成 :app :exposedTo :vuln1,这是一个新三元组,
因此将其添加。目前还没有 :frontend 的解,因为
本次开始对 R2 求值时,
:app :exposedTo :vuln1 尚不在 GE 中。
R3:此时 ?x :exposedTo ?v 可与 :db 和
:app 匹配;
将其与 :vuln1 :severity 9.1 连接并应用
FILTER(?s >= 9.0) 后,两个解均被保留。
规则头生成 :db :status :criticallyExposed 和
:app :status :criticallyExposed,二者均为新三元组。
# 第 0 层第一轮后的 GI
:db :exposedTo :vuln1 . # 新增(R1)
:app :exposedTo :vuln1 . # 新增(R2)
:db :status :criticallyExposed . # 新增(R3)
:app :status :criticallyExposed . # 新增(R3)
生成了新的三元组,因此继续对第 0 层 进行下一轮求值。
第 0 层,第二轮。
R1:与之前相同地匹配;:db :exposedTo :vuln1 已经
位于 GE 中,因此不会生成新内容。
R2:此时连接还会产生
?x = :frontend、?y = :app 和 ?v = :vuln1,从而生成
新三元组 :frontend :exposedTo :vuln1。
暴露关系经过两轮才沿
依赖链传播两步。
R3:生成新三元组
:frontend :status :criticallyExposed。
# 第 0 层第二轮后的 GI
:db :exposedTo :vuln1 .
:app :exposedTo :vuln1 .
:frontend :exposedTo :vuln1 . # 新增(R2)
:db :status :criticallyExposed .
:app :status :criticallyExposed .
:frontend :status :criticallyExposed . # 新增(R3)
第 0 层,第三轮。
此时每条规则的每个解都只会生成
已经位于 GE 中的三元组。GI 保持不变。
没有新的三元组意味着第 0 层已完成:所有
能够推导出的 :exposedTo 三元组和 :status :criticallyExposed 三元组
均已推导出来。
第 1 层,仅运行一次的规则。 首先对该层的仅运行一次的规则求值, 每条规则恰好求值一次。
R5:规则体与三个 :status :criticallyExposed
三元组匹配,产生解 ?x = :db、?x = :app 和
?x = :frontend。对于每个解,实例化规则头
都会创建一个新的空白节点,每个解生成两个
三元组:为每个严重暴露
的组件生成一条通知。即使该层的一般规则随后会
反复求值,也不会再次对 R5 求值。
第 1 层,一般规则的第一轮。
R4:模式 ?x rdf:type :Component 与
:frontend、:app、:db 和 :logger 匹配。
(它不与 R5 刚刚创建的
rdf:type :Notification 三元组匹配。)
否定元素 NOT { ?x :status :criticallyExposed } 会拒绝
:frontend、:app 和 :db 的解,因为
GE 中分别包含它们各自的 :status :criticallyExposed 三元组。
只有解 ?x = :logger 被保留下来,从而生成新的
三元组 :logger :status :safeToDeploy。
# 第 1 层后的 GI
:db :exposedTo :vuln1 .
:app :exposedTo :vuln1 .
:frontend :exposedTo :vuln1 .
:db :status :criticallyExposed .
:app :status :criticallyExposed .
:frontend :status :criticallyExposed .
_:n1 rdf:type :Notification . # 新增(R5)
_:n1 :concerns :db . # 新增(R5)
_:n2 rdf:type :Notification . # 新增(R5)
_:n2 :concerns :app . # 新增(R5)
_:n3 rdf:type :Notification . # 新增(R5)
_:n3 :concerns :frontend . # 新增(R5)
:logger :status :safeToDeploy . # 新增(R4)
第 1 层,一般规则的第二轮。
没有新的三元组。求值完成,上面所示的 GI
即为所得的推理图。
正是分层使该结果可靠。
如果在第 0 层第一轮期间、
尚未推导出 :frontend :status :criticallyExposed 时对 R4 求值,
它就会错误地得出 :frontend :status :safeToDeploy,而
R5 会为 :db 和 :app 创建通知,
却不会为 :frontend 创建通知。
将二者推迟到第 1 层,意味着它们可以看到完整的
:status :criticallyExposed 三元组集合,因此每一层中
规则的求值顺序不会影响
最终结果。
SPARQL-RL 文档
是一个RDF
字符串,
使用 UTF-8 [RFC3629] 编码,并以
RuleSet
产生式开头,并符合
7.6 语法中定义的附加约束。
仅允许使用Unicode 标量值,
范围为 U+0000 到 U+D7FF
以及 U+E000 到 U+10FFFF。
这排除了
代理代码点,
范围为 U+D800 到 U+DFFF。
版本标签是一个字符串,用于标识形状规则语言的语法和语义 一致性 版本。
| 版本标签 |
|---|
| "1.2" |
版本声明应该在文档的较前位置给出。
一个SPARQL-RL 文档中
可以出现多个VERSION指令。
每个指令适用于该指令之后的文档部分,
直到遇到另一个指令或到达文档末尾。
版本标签也可以由
媒体类型的 version
参数给出。在当前不存在
VERSION 指令的情况下,将考虑
作为媒体类型一部分指定的版本。
空白
(产生式 WS)用于
分隔两个终结符,否则它们会被(错误地)识别为一个
终结符。下方以大写字母表示的规则名称指示空白
具有重要意义的位置;这些规则构成了用于构建
形状规则语言解析器的一种可能的终结符选择。
空白在产生式
String 中具有重要意义。
注释以位于
IRIREF、
STRING_LITERAL1、
STRING_LITERAL2、
STRING_LITERAL_LONG1 或
STRING_LITERAL_LONG2
之外的#开始,
并持续到行尾(由
LF 或
CR 标记),
如果注释标记之后没有行尾,则持续到文件末尾。
注释被视为空白。
相对 IRI 引用按照 统一资源标识符(URI):通用 语法 [RFC3986],仅使用 第 5.2 节中的基本算法,基于基础 IRI 进行解析。 不执行基于语法的规范化,也不执行基于方案的规范化 (在 RFC3986 第 6.2.2 和 6.2.3 节中描述)。 根据 国际化资源标识符 (IRI) [RFC3987] 第 6.5 节, IRI 引用中额外允许的字符按照 URI 引用中 非保留字符的相同方式处理。
BASE
指令定义用于按照 [RFC3986]
解析相对 IRI
引用的基础 IRI,
即第 5.1.1 节“嵌入内容中的基础 URI”
。
第 5.1.2 节“来自
封装实体的基础 URI”
定义了作用域内基础 IRI 如何来自封装文档,
例如具有 xml:base 指令的 SOAP 信封,或具有
Content-Location 标头的 MIME 多部分文档。
第 5.1.3 节“来自检索
URI 的基础 URI”中标识的“检索 URI”,
是特定SPARQL-RL 文档被检索时所使用的 URL。
如果上述内容均未指定基础 URI,则使用默认
基础 URI(第 5.1.4 节“默认基础 URI”)。
每个 BASE 指令都会相对于前一个作用域内基础 URI
设置一个新的作用域内基础 URI。
SRL 文档中使用三种形式的转义:
数值转义序列表示 Unicode 码点的值。
数值转义序列不得产生位于
U+D800 到 U+DFFF 范围内的码点值,
该范围是 Unicode
代理码点的范围。
| 转义序列 | Unicode 码点 |
|---|---|
\u hex
hex
hex
hex
|
位于 U+0000 到 U+D7FF
以及 U+E000 到 U+FFFF
范围内的Unicode 码点,
与四个十六进制数字从最高有效位到最低有效位进行解释后所编码的值相对应。 |
\U hex
hex
hex
hex
hex
hex
hex
hex
|
位于 U+0000 到
U+D7FF
以及 U+E000 到 U+10FFFF
范围内的Unicode 码点,
与八个十六进制数字
从最高有效位到最低有效位进行解释后所编码的值相对应。
|
其中 hex 是十六进制字符
HEX ::= [0-9] | [A-F] |
[a-f]
字符串转义序列表示传统上 在字符串字面量中进行转义的字符:
| 转义序列 | Unicode 码点 |
|---|---|
\t |
U+0009 |
\b |
U+0008 |
\n |
U+000A |
\r |
U+000D |
\f |
U+000C |
\" |
U+0022 |
\' |
U+0027 |
\\ |
U+005C |
保留字符转义序列由一个
\ 后跟
这些字符之一 ~.-!$&'()*+,;=/?#@%_ 构成,并
表示位于
\右侧的字符。
| 数值 转义 |
字符串 转义 |
保留字符 转义 |
|
|---|---|---|---|
IRI,
用作 RDF 项、
PREFIX
或 BASE 声明
|
是 | 否 | 否 |
| 局部名称 | 否 | 否 | 是 |
| 字符串 | 是 | 是 | 否 |
此处使用的 EBNF 定义于 XML 1.0 [EBNF-NOTATION]。
注:
[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
|
[12] |
Rule1 |
::= | 'RULE' iri? HeadTemplate ForClause? 'WHERE' 'DATA'? BodyPattern
|
[13] |
Rule2 |
::= | 'IF' iri? ForClause? 'DATA'? BodyPattern 'THEN' HeadTemplate
|
[14] |
Data |
::= | 'DATA' '{' DataTriplesBlock? '}'
|
[15] |
HeadTemplate |
::= | '{' HeadTemplateBlock? '}'
|
[16] |
ForClause |
::= | 'FOR' Var 'IN' iri
|
[17] |
BodyPattern |
::= | '{' BodyTriplesBlock? ( BodyNotTriples '.'? BodyTriplesBlock? )* '}'
|
[18] |
BodyNotTriples |
::= | Filter | Negation | Assignment
|
[19] |
Filter |
::= | 'FILTER' Constraint
|
[20] |
Constraint |
::= | BrackettedExpression | BuiltInCall | FunctionCall
|
[21] |
FunctionCall |
::= | iri ArgList
|
[22] |
ArgList |
::= | NIL | '(' Expression ( ',' Expression )* ')'
|
[23] |
ExpressionList |
::= | NIL | '(' Expression ( ',' Expression )* ')'
|
[24] |
Negation |
::= | 'NOT' 'DATA'? '{' BodyBasic '}'
|
[25] |
BodyBasic |
::= | BodyTriplesBlock? ( BodyBasicNotTriples '.'? BodyTriplesBlock? )*
|
[26] |
BodyBasicNotTriples
|
::= | Filter |
[27] |
Assignment |
::= | 'SET' '(' Var ':=' Expression ')'
|
[28] |
DataTriplesBlock
|
::= | TriplesSameSubjectData ( '.' DataTriplesBlock? )?
|
[29] |
TriplesSameSubjectData
|
::= | RDFTermData PropertyListNotEmptyData | TriplesNodeData PropertyListData | ReifiedTripleBlockData
|
[30] |
PropertyListData
|
::= | PropertyListNotEmptyData?
|
[31] |
PropertyListNotEmptyData
|
::= | VerbData ObjectListData ( ';' ( VerbData ObjectListData )? )*
|
[32] |
VerbData |
::= | iri | 'a'
|
[33] |
ObjectListData |
::= | ObjectData ( ',' ObjectData )*
|
[34] |
ObjectData |
::= | GraphNodeData AnnotationData
|
[35] |
GraphNodeData |
::= | RDFTermData | TriplesNodeData | ReifiedTripleData
|
[36] |
TriplesNodeData |
::= | CollectionData | BlankNodePropertyListData
|
[37] |
BlankNodePropertyListData
|
::= | '[' PropertyListNotEmptyData ']'
|
[38] |
CollectionData |
::= | '(' GraphNodeData+ ')'
|
[39] |
AnnotationData |
::= | ( ReifierData | AnnotationBlockData )*
|
[40] |
AnnotationBlockData
|
::= | '{|' PropertyListNotEmptyData '|}'
|
[41] |
ReifierData |
::= | '~' ReifierIdData?
|
[42] |
ReifierIdData |
::= | iri | BlankNode
|
[43] |
ReifiedTripleBlockData
|
::= | ReifiedTripleData PropertyListData
|
[44] |
ReifiedTripleData
|
::= | '<<' ReifiedTripleSubjectData VerbData ReifiedTripleObjectData ReifierData? '>>'
|
[45] |
ReifiedTripleSubjectData
|
::= | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | ReifiedTripleData | TripleTermData
|
[46] |
ReifiedTripleObjectData
|
::= | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | ReifiedTripleData | TripleTermData
|
[47] |
TripleTermData |
::= | '<<(' TripleTermSubjectData VerbData TripleTermObjectData ')>>'
|
[48] |
TripleTermSubjectData
|
::= | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | TripleTermData
|
[49] |
TripleTermObjectData
|
::= | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | TripleTermData
|
[50] |
HeadTemplateBlock
|
::= | TriplesBlockTemplate
|
[51] |
TriplesBlockTemplate
|
::= | TriplesSameSubjectTemplate ( '.' TriplesBlockTemplate? )?
|
[52] |
TriplesSameSubjectTemplate
|
::= | VarOrRDFTerm PropertyListNotEmptyTemplate | TriplesNodeTemplate PropertyListTemplate | ReifiedTripleBlockTemplate
|
[53] |
PropertyListTemplate
|
::= | PropertyListNotEmptyTemplate?
|
[54] |
PropertyListNotEmptyTemplate
|
::= | Verb ObjectListTemplate ( ';' ( Verb ObjectListTemplate )? )*
|
[55] |
ObjectListTemplate
|
::= | ObjectTemplate ( ',' ObjectTemplate )*
|
[56] |
ObjectTemplate |
::= | GraphNodeTemplate AnnotationTemplate
|
[57] |
GraphNodeTemplate
|
::= | VarOrRDFTerm | TriplesNodeTemplate | ReifiedTriple
|
[58] |
TriplesNodeTemplate
|
::= | CollectionTemplate | BlankNodePropertyListTemplate
|
[59] |
BlankNodePropertyListTemplate
|
::= | '[' PropertyListNotEmptyTemplate ']'
|
[60] |
CollectionTemplate
|
::= | '(' GraphNodeTemplate+ ')'
|
[61] |
AnnotationTemplate
|
::= | ( Reifier | AnnotationBlockTemplate )*
|
[62] |
AnnotationBlockTemplate
|
::= | '{|' PropertyListNotEmptyTemplate '|}'
|
[63] |
ReifiedTripleBlockTemplate
|
::= | ReifiedTriple PropertyListTemplate
|
[64] |
BodyTriplesBlock
|
::= | TriplesBlockPattern
|
[65] |
TriplesBlockPattern
|
::= | TriplesSameSubjectPattern ( '.' TriplesBlockPattern? )?
|
[66] |
ReifiedTripleBlockPattern
|
::= | ReifiedTriple PropertyListPattern
|
[67] |
TriplesSameSubjectPattern
|
::= | VarOrRDFTerm PropertyListNotEmptyPattern | TriplesNodePattern PropertyListPattern | ReifiedTripleBlockPattern
|
[68] |
PropertyListPattern
|
::= | PropertyListNotEmptyPattern?
|
[69] |
PropertyListNotEmptyPattern
|
::= | ( VerbPath | Var ) ObjectListPattern ( ';' ( ( VerbPath | Var ) ObjectListPattern )? )*
|
[70] |
ObjectListPattern
|
::= | ObjectPattern ( ',' ObjectPattern )*
|
[71] |
ObjectPattern |
::= | GraphNodePattern AnnotationPattern
|
[72] |
TriplesNodePattern
|
::= | CollectionPattern | BlankNodePropertyListPattern
|
[73] |
BlankNodePropertyListPattern
|
::= | '[' PropertyListNotEmptyPattern ']'
|
[74] |
CollectionPattern
|
::= | '(' GraphNodePattern+ ')'
|
[75] |
AnnotationPattern
|
::= | ( Reifier | AnnotationBlockPattern )*
|
[76] |
AnnotationBlockPattern
|
::= | '{|' PropertyListNotEmptyPattern '|}'
|
[77] |
GraphNodePattern
|
::= | VarOrRDFTerm | TriplesNodePattern | ReifiedTriple
|
[78] |
Reifier |
::= | '~' ReifierId?
|
[79] |
ReifierId |
::= | Var | iri | BlankNode
|
[80] |
ReifiedTriple |
::= | '<<' ReifiedTripleSubject Verb ReifiedTripleObject Reifier? '>>'
|
[81] |
ReifiedTripleSubject
|
::= | Var | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | ReifiedTriple | TripleTerm
|
[82] |
ReifiedTripleObject
|
::= | Var | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | ReifiedTriple | TripleTerm
|
[83] |
TripleTerm |
::= | '<<(' TripleTermSubject Verb TripleTermObject ')>>'
|
[84] |
TripleTermSubject
|
::= | Var | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | TripleTerm
|
[85] |
TripleTermObject
|
::= | Var | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | TripleTerm
|
[86] |
Verb |
::= | VarOrIri | 'a'
|
[87] |
VerbPath |
::= | Path |
[88] |
Path |
::= | PathSequence |
[89] |
PathSequence |
::= | PathEltOrInverse ( '/' PathEltOrInverse )*
|
[90] |
PathEltOrInverse
|
::= | PathElt | '^' PathElt
|
[91] |
PathElt |
::= | ( iri | 'a' | '(' Path ')' )
|
[92] |
RDFTermData |
::= | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | NIL | TripleTermData
|
[93] |
VarOrRDFTerm |
::= | Var | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | NIL | TripleTerm
|
[94] |
VarOrIri |
::= | Var | iri
|
[95] |
Var |
::= | VAR1 | VAR2
|
[96] |
RDFLiteral |
::= | String ( LANG_DIR | '^^' iri )?
|
[97] |
NumericLiteral |
::= | NumericLiteralUnsigned | NumericLiteralPositive | NumericLiteralNegative
|
[98] |
NumericLiteralUnsigned
|
::= | INTEGER | DECIMAL | DOUBLE
|
[99] |
NumericLiteralPositive
|
::= | INTEGER_POSITIVE | DECIMAL_POSITIVE | DOUBLE_POSITIVE
|
[100] |
NumericLiteralNegative
|
::= | INTEGER_NEGATIVE | DECIMAL_NEGATIVE | DOUBLE_NEGATIVE
|
[101] |
BooleanLiteral |
::= | 'true' | 'false'
|
[102] |
String |
::= | STRING_LITERAL1 | STRING_LITERAL2 | STRING_LITERAL_LONG1 | STRING_LITERAL_LONG2
|
[103] |
iri |
::= | IRIREF | PrefixedName
|
[104] |
PrefixedName |
::= | PNAME_LN | PNAME_NS
|
[105] |
BlankNode |
::= | BLANK_NODE_LABEL | ANON
|
[106] |
Expression |
::= | ConditionalOrExpression
|
[107] |
ConditionalOrExpression
|
::= | ConditionalAndExpression ( '||' ConditionalAndExpression )*
|
[108] |
ConditionalAndExpression
|
::= | ValueLogical ( '&&' ValueLogical )*
|
[109] |
ValueLogical |
::= | RelationalExpression
|
[110] |
RelationalExpression
|
::= | NumericExpression ( '=' NumericExpression | '!=' NumericExpression | '<' NumericExpression | '>' NumericExpression | '<=' NumericExpression | '>=' NumericExpression | 'IN' ExpressionList | 'NOT' 'IN' ExpressionList )?
|
[111] |
NumericExpression
|
::= | AdditiveExpression
|
[112] |
AdditiveExpression
|
::= | MultiplicativeExpression ( '+' MultiplicativeExpression | '-' MultiplicativeExpression | ( NumericLiteralPositive | NumericLiteralNegative ) ( ( '*' UnaryExpression ) | ( '/' UnaryExpression ) )* )*
|
[113] |
MultiplicativeExpression
|
::= | UnaryExpression ( '*' UnaryExpression | '/' UnaryExpression )*
|
[114] |
UnaryExpression |
::= | '!' PrimaryExpression
|
[115] |
PrimaryExpression
|
::= | BrackettedExpression | BuiltInCall | iriOrFunction | RDFLiteral | NumericLiteral | BooleanLiteral | Var | ExprTripleTerm
|
[116] |
iriOrFunction |
::= | iri ArgList?
|
[117] |
ExprTripleTerm |
::= | '<<(' ExprTripleTermSubject Verb ExprTripleTermObject ')>>'
|
[118] |
ExprTripleTermSubject
|
::= | iri | RDFLiteral | NumericLiteral | BooleanLiteral | Var
|
[119] |
ExprTripleTermObject
|
::= | iri | RDFLiteral | NumericLiteral | BooleanLiteral | Var | ExprTripleTerm
|
[120] |
BrackettedExpression
|
::= | '(' Expression ')'
|
[121] |
BuiltInCall |
::= | 'STR' '(' Expression ')'
|
终结符的产生式:
[122] |
IRIREF |
::= | '<' ([^<>"{}|^`\]-[#x00-#x20] | UCHAR )* '>'
|
[123] |
PNAME_NS |
::= | PN_PREFIX? ':' |
[124] |
PNAME_LN |
::= | PNAME_NS PN_LOCAL
|
[125] |
BLANK_NODE_LABEL
|
::= | '_:' ( PN_CHARS_U | [0-9] ) ((PN_CHARS|'.')* PN_CHARS)?
|
[126] |
VAR1 |
::= | '?' VARNAME |
[127] |
VAR2 |
::= | '$' VARNAME |
[128] |
LANG_DIR |
::= | '@' [a-zA-Z]+ ('-' [a-zA-Z0-9]+)* ('--' [a-zA-Z]+)?
|
[129] |
INTEGER |
::= | [0-9]+ |
[130] |
DECIMAL |
::= | [0-9]* '.' [0-9]+ |
[131] |
DOUBLE |
::= | ( ([0-9]+ ('.'[0-9]*)? ) | ( '.' ([0-9])+ ) ) [eE][+-]?[0-9]+
|
[132] |
INTEGER_POSITIVE
|
::= | '+' INTEGER
|
[133] |
DECIMAL_POSITIVE
|
::= | '+' DECIMAL
|
[134] |
DOUBLE_POSITIVE |
::= | '+' DOUBLE
|
[135] |
INTEGER_NEGATIVE
|
::= | '-' INTEGER
|
[136] |
DECIMAL_NEGATIVE
|
::= | '-' DECIMAL
|
[137] |
DOUBLE_NEGATIVE |
::= | '-' DOUBLE
|
[138] |
STRING_LITERAL1 |
::= | "'" ( ([^#x27#x5C#xA#xD]) | ECHAR | UCHAR )* "'"
|
[139] |
STRING_LITERAL2 |
::= | '"' ( ([^#x22#x5C#xA#xD]) | ECHAR | UCHAR )* '"'
|
[140] |
STRING_LITERAL_LONG1
|
::= | "'''" ( ( "'" | "''" )? ( [^'\] | ECHAR | UCHAR ) )* "'''"
|
[141] |
STRING_LITERAL_LONG2
|
::= | '"""' ( ( '"' | '""' )? ( [^"\] | ECHAR | UCHAR ) )* '"""'
|
[142] |
ECHAR |
::= | '\' [tbnrf\"'] |
[143] |
UCHAR |
::= | ('\u' HEX HEX HEX HEX) | ('\U' HEX HEX HEX HEX HEX HEX HEX HEX)
|
[144] |
NIL |
::= | '(' WS* ')' |
[145] |
WS |
::= | #x20 | #x9 | #xD | #xA |
[146] |
ANON |
::= | '[' WS* ']' |
[147] |
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]
|
[148] |
PN_CHARS_U |
::= | PN_CHARS_BASE | '_'
|
[149] |
VARNAME |
::= | ( PN_CHARS_U | [0-9] ) ( PN_CHARS_U | [0-9] | #x00B7 | [#x0300-#x036F] | [#x203F-#x2040] )*
|
[150] |
PN_CHARS |
::= | PN_CHARS_U | '-' | [0-9] | #x00B7 | [#x0300-#x036F] | [#x203F-#x2040]
|
[151] |
PN_PREFIX |
::= | PN_CHARS_BASE ((PN_CHARS|'.')* PN_CHARS)?
|
[152] |
PN_LOCAL |
::= | (PN_CHARS_U | ':' | [0-9] | PLX ) ((PN_CHARS | '.' | ':' | PLX)* (PN_CHARS | ':' | PLX) )?
|
[153] |
PLX |
::= | PERCENT | PN_LOCAL_ESC
|
[154] |
PERCENT |
::= | '%' HEX HEX
|
[155] |
HEX |
::= | [0-9] | [A-F] | [a-f] |
[156] |
PN_LOCAL_ESC |
::= | '\' ( '_' | '~' | '.' | '-' | '!' | '$' | '&' | "'" | '(' | ')' | '*' | '+' | ',' | ';' | '=' | '/' | '?' | '#' | '@' | '%' )
|
此语法的文本版本可在 此处获取。
本文档使用了一些特定的终结符字面量字符串 [EBNF-NOTATION]。为了明确这些 终结符字面量字符串所使用的 Unicode 码点,下表描述了本节中使用的特定 字符。
| 代码 | 字形 | 描述 |
|---|---|---|
U+000A |
LF |
换行符 |
U+000D |
CR |
回车符 |
U+0023 |
# |
数字符号 |
U+0025 |
% |
百分号 |
U+005C |
\ |
反斜杠 |
以下算法展示了一种通过 递归访问所有被引用文档来解析导入语句的方法。
两个规则集 RS1 和 RS2 的规则集
合并
是一个规则集 MR,其定义如下:
define ruleSetMerge(rule set R1,rule Set RS2):
MR.rules = RS1.rules ∪︀ RS2.rules
MR.data = rdf_merge(RS1.data, RS2.data)
MR.imports = {}
the result is MR
enddefine
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 be a rule set formed from RS.rules and RS.data
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
the result is RS2
enddefine
let RS be a rule set
let V = {}
if RS has a location, V = { location of RS }
result is imports(RS, V)
其中 rdf_merge 是
RDF
合并
操作。
形状规则语言的互联网媒体类型(以前称为 MIME 类型)
是
"application/sparql-rl"。
以下信息已提交给互联网工程 指导组(IESG)进行审查、批准并向 IANA 注册。
versionversion 的可接受值定义于
版本标签中。
profileprofile 参数的值是一个
以空格分隔的非空 URI 列表。
有关更多信息和背景,请参阅 [RFC6906]。
本节是非规范性的。
待办
SRL 文档可以包含引用其他SRL 文档的 IMPORTS 语句。
如果导入的文档本身包含其自己的 IMPORTS 语句,
则也会导入这些文档。
虽然使用 IMPORTS 语句有助于将规则集模块化,但也可能
引入安全风险。
风险包括但不限于:规则集
在求值期间无论是恶意还是意外地导致过量计算;
以及 HTTP 请求被拦截并返回不同的文档,
包括过时的副本。
请参阅4.5 处理导入。
需要有关安全注意事项的意见。
本节是非规范性的。
待办
本节是非规范性的。
待办
本节是非规范性的。
待办
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in:
Referenced in: