Copyright © 2025-2026 World Wide Web Consortium. W3C® liability, trademark and permissive document license rules apply.
本文档定义了 SPARQL-RL,一种用于 RDF 的 Datalog 风格规则语言。
SPARQL-RL 通过生成新的 RDF 数据来提供推理,这些数据源自 一组规则与基础数据 RDF 图的组合。它 提供类似 SPARQL 的文本语法,并定义一组 规则如何针对 RDF 图进行求值。
SPARQL 规则语言可简称为 SRL,前提是 上下文明确。
本规范由 数据形状工作组发布。
本节说明本文档在 发布时的状态。当前 W3C 出版物列表以及本技术报告的最新修订版可在 以下 W3C 标准和草案 索引中找到。
本文档由数据形状 工作组作为 工作草案发布,采用 推荐标准 轨道。
作为 工作草案发布并不意味着 得到 W3C 及其成员的认可。
这是一份草案文档,随时可能由其他 文档更新、取代或废止。除将其作为 正在进行的工作外,不宜引用本文档。 此即将发布的推荐标准的未来更新可能会纳入 新功能。
本文档由一个 根据 W3C 专利 政策运作的小组编制。 W3C 维护着 所有专利 披露的公开列表, 这些披露与该小组的交付成果有关; 该页面还包含 披露专利的说明。实际 知晓某项专利,并认为该专利包含 必要权利要求的个人, 必须依照 W3C 专利政策第 6 节披露相关信息。
本文档受 2025年8月18日 W3C 流程文档约束。
本规范是 SHACL 1.2 规范系列的一部分。有关它们更详细的 介绍,请参阅 SHACL 1.2 概述。
这些规范如下:
工作草案:
工作组说明草案:
实现者可以通过成功通过 SHACL 1.2 测试套件的测试用例, 部分检查其对上述规范的一致性水平。 但请注意,通过测试套件中的所有测试并不意味着完全符合这些 规范。 它仅意味着实现符合测试套件所测试的方面。
本文档介绍 SPARQL-RL。它是一种通过声明式规则 从现有 RDF 数据派生新 RDF 三元组的机制。 本文档定义基于规则的推理的语法和语义。
SPARQL-RL 的实现提供两种操作。 infer 操作将规则应用于给定的 基础图,并生成包含通过执行规则所派生的 RDF 三元组的推理图。 是否将推理图与基础图合并是可选的, 由用户决定。query 操作用于确定 是否可以使用规则从基础图派生给定的目标模式。
SPARQL-RL 允许使用新的 RDF 项,包括可在 规则头的三元组模板中使用的空白节点。
SPARQL-RL 还支持否定即失败,这可能导致 根据规则执行顺序的不同而产生不同的推理图。 为避免这种情况,规则使用 分层技术进行求值,该技术在规则之间建立单一的隐式 顺序,从而确保始终生成相同的推理图。
以下其他规范提供了本文档所使用的 基础术语:
本文档中的 RDF 数据示例使用 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/sparql-rl# |
xsd: |
http://www.w3.org/2001/XMLSchema# |
sparql: |
http://www.w3.org/ns/sparql# |
ex: |
http://example/ |
整篇文档中将出现包含 RDF 图的彩色编码框, 其中使用 Turtle。这些 Turtle 文档片段使用上面给出的前缀 绑定。
# 此框表示规则
# 此框表示输入数据
# 此框表示推断数据
除标记为非规范性的章节外,本规范中的所有编写指南、图表、示例和注释均为非规范性内容。 本规范中的其他所有内容均为规范性内容。
本文档中的关键词 可以、必须、不得 和应该 应按照 BCP 14 [RFC2119] [RFC8174] 中所述的方式解释,但当且仅当它们 像此处所示以全部大写形式出现时才应如此。
本规范为以下内容定义一致性标准:
符合要求的 SRL 文档是
符合语法的 RDF
字符串,该语法从
RuleSet
产生式开始,如
7. SPARQL-RL 语法中所定义。
本规范未定义 SPARQL-RL 处理器 如何处理不符合要求的 规则集。
本节是非规范性的。
给定一个基础图和一个规则集,SPARQL-RL 会推断出新的三元组。 求值的输出是一个推理图,其中包含 未出现在基础图中的派生三元组。
每个规则都有一个称为规则体的模式,以及一个称为 规则头的结果模板。 执行规则时,会查找规则体中各变量的值, 使规则体与合并后的基础图以及 截至此时执行过程中推断出的所有三元组相匹配。 随后使用这些值实例化 规则头中的三元组模板,以生成新的推断三元组。
规则会持续执行,直至不再推断出更多三元组; 随着新的推断三元组变为可用,规则可能会执行多次。
SPARQL-RL 执行的定义方式确保规则执行顺序 在创建新的 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 操作相同,
并且 SPARQL-RL 提供许多与 SPARQL 相同的函数和运算符。
否定允许指定一个不得匹配的模式。 这称为“否定即失败”。
为了对否定元素求值,规则求值算法 会确保所有可能生成与否定元素中的模式 相匹配的三元组的规则均已执行完毕。这称为 分层,并确保否定基于 所有相关的可能三元组,无论这些三元组来自数据还是 否定元素中的内容都已完成。包含否定的规则被称为_依赖_于 生成这些三元组的规则(参见下文的4.3 规则依赖关系)。
赋值允许将表达式的结果赋给规则体中的变量。 这可用于根据数据创建新的 RDF 项。
空白节点可以在规则头中使用,并且每当规则求值生成三元组时, 每个空白节点都会生成一个全新的空白节点。
涉及赋值的规则,以及在其 规则头中创建空白节点的规则,都是仅运行一次的规则。 此类规则会在所有可能产生其所依赖数据的规则之后运行, 并在任何依赖其所产生数据的规则之前运行。
此条件可确保此类规则不会循环回自身, 从而导致产生无界数量的 RDF 项。
SPARQL-QL 规则集可以通过在该规则集的 规则集导入项中包含其他规则集的 URL 来纳入它们。 这允许将规则组织为可在规则集之间共享的库。
规则集中的 IMPORTS 语句会在对该规则集中的任何规则
求值之前处理。在导入步骤中,如果导入的规则集
自身也有导入项,则也会递归处理这些导入项。
在处理规则集期间遍历 IMPORTS 语句可能会导致
循环导入。一个规则集只会导入一次;
导入语句图中的循环不会导致无限循环。
SRL 处理器可以选择是否支持导入规则集。 更多详细信息,请参阅4.5 处理 导入。
数据块允许直接向规则集求值简洁地提供 RDF 三元组。 数据块中的三元组会添加到推理图中,并可用于 在规则体中进行匹配。
例如,规则集可以直接附带已断言的已知事实, 而无需从数据中派生:
数据块等同于一条规则体为空的规则: 无需对任何规则求值,其三元组便会成为推理图的 一部分。
规则组织在规则集中。 规则集和数据图(即基础图)是求值的输入。 输出是一个称为推理图的图, 它是未出现在数据图中的三元组集合。
求值期间,根据一条规则推断出的三元组 可用于在其他规则中进行匹配。 规则集求值会持续进行,直至推理图 包含可从规则集和数据图的输入中获得的 所有可能三元组。
求值开始时需要先执行两个步骤, 在对规则本身求值之前 准备规则集:
规则集准备完毕后,求值将 按顺序采用分层中的每一层, 对该层中的规则进行完整求值, 然后再进入下一层。
基数据是基础图中的三元组。 在规则集求值期间,可能需要 仅对原始三元组而非任何推断出的 三元组进行模式匹配。
设置默认值就是一个示例。 规则必须测试基础图是否已经包含某个值;如果不包含, 则规则可以计算一个默认值。
使用 NOT DATA 允许规则体的其他部分匹配推断出的三元组。
也可以针对基础图执行所有规则体匹配,方法是
使用 WHERE DATA。
此类规则仅依赖于基础图中的匹配。
SPARQL-RL 抽象语法是 SPARQL-RL 的逻辑结构。 它用于定义 SPARQL-RL 的执行算法。
SPARQL-RL 提供两种操作:infer 和query。
查询是确定给定目标 模式是否能够使用基础图和规则集推导出来的操作。 它可以不对所有规则求值;相反,它可以只对 回答查询目标所必需的规则求值。
在三元组模式或三元组 模板中, 元组的位置 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 为 V0 与所有 varsj 的并集, 其中 j 为从 1 到 N 的 j,N 为该序列的长度。
给定变量集合 V0,如果满足以下条件, 则良构 序列是一个规则元素序列:
如果规则体序列在 V0 为空集的情况下是良构序列, 并且规则头的三元组模板中的每个变量 都是 Vall 的元素,则规则是良构 规则。
如果第二条规则的输出会影响第一条规则的规则体求值,
则规则 R1 依赖于规则 R2。也就是说,R2 的规则头
有一个可能生成与 R1 规则体中的
三元组模式相匹配的三元组的三元组模板,
该三元组模式要么作为三元组模式元素出现,要么位于否定元素内部。
依赖关系分为两种:
闭合依赖和开放
依赖。
闭合依赖确保规则 R2
在规则 R1 执行前已生成其所有可能的输出。
如果规则依赖关系不是闭合依赖,则它是开放依赖,
这允许第一条规则 R1 在
规则 R2 可能再次运行以生成更多三元组时执行,
随后这些三元组可能导致使用来自 R2 的新三元组对 R1 重新求值。
在第一个示例中,第一条规则对 第二条规则具有开放依赖。
在第二个示例中,第一条规则对
第二条规则具有闭合依赖:三元组模式 ?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 的表达式,还是用于
规则头的三元组模板中。SPARQL CONSTRUCT 查询和
INSERT 更新会在变量出现在 CONSTRUCT 或
INSERT 模板中,但未在 WHERE 子句中赋值时
生成部分结果。
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(μ, triple pattern),
它返回一个三元组模式,
其中,对于三元组模式中
位于
dom(μ)
内的每个变量出现位置,都会将其替换为
解映射为 var 给出的RDF
项。
如果生成的三元组模式没有变量,则它是一个 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.
## μ is a solution sequence
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, μ), ..., evalFunction(exprN, μ))
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 ∪︀ 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 文档
是一个以 UTF-8 [RFC3629] 编码的
RDF
字符串,该字符串以
RuleSet
产生式开头,并符合
7.6 语法中定义的附加约束。
只允许使用
Unicode 标量值,其范围为
U+0000 至 U+D7FF
以及 U+E000 至 U+10FFFF。
这排除了范围为
U+D800 至 U+DFFF 的
代理代码点。
版本标签是用于标识 SPARQL-RL 语法和语义 一致性的字符串。
| 版本标签 |
|---|
| "1.2" |
版本声明应该在文档前部给出。
一个SPARQL-RL 文档中
可以出现多个 VERSION 指令。
每个指令都适用于文档中该指令之后的部分,
直到遇到另一个指令或到达文档末尾。
版本标签也可以由
媒体类型的 version
参数给出。在没有当前
VERSION 指令的情况下,将采用
作为媒体类型一部分指定的版本。
空白符
(产生式 WS)用于
分隔两个原本会被(错误地)识别为一个
终结符的终结符。下面以大写形式书写的规则名称指示空白符
具有重要意义的位置;这些名称构成了用于构建
SPARQL-RL 解析器的一种可能的终结符选择。
空白符在产生式
String中具有重要意义。
注释以位于
IRIREF、
STRING_LITERAL1、
STRING_LITERAL2、
STRING_LITERAL_LONG1或
STRING_LITERAL_LONG2
之外的#开头,并持续到行尾(以
LF或
CR标记);
如果注释标记之后没有行尾,则持续到文件末尾。
注释被视为空白符。
根据 统一资源标识符(URI):通用 语法 [RFC3986], 相对 IRI 引用使用基础 IRI 进行解析,并且仅使用第 5.2 节中的 基本算法。 不执行基于语法的规范化或基于方案的规范化 (见 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 相对于前一个基础 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 |
| 数字 转义 |
字符串 转义 |
保留字符 转义 |
|
|---|---|---|---|
用作 RDF 项、
PREFIX
或 BASE 声明的
IRI
|
是 | 否 | 否 |
| 本地名称 | 否 | 否 | 是 |
| 字符串 | 是 | 是 | 否 |
此处使用的 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 |
::= | 'RULE' iri? HeadTemplate 'WHERE' 'DATA'? BodyPattern
|
[12] |
Data |
::= | 'DATA' '{' DataTriplesBlock? '}'
|
[13] |
HeadTemplate |
::= | '{' HeadTemplateBlock? '}'
|
[14] |
BodyPattern |
::= | '{' BodyTriplesBlock? ( BodyNotTriples '.'? BodyTriplesBlock? )* '}'
|
[15] |
BodyNotTriples |
::= | Filter | Negation | Assignment
|
[16] |
Filter |
::= | 'FILTER' Constraint
|
[17] |
Constraint |
::= | BrackettedExpression | BuiltInCall | FunctionCall
|
[18] |
FunctionCall |
::= | iri ArgList
|
[19] |
ArgList |
::= | NIL | '(' Expression ( ',' Expression )* ')'
|
[20] |
ExpressionList |
::= | NIL | '(' Expression ( ',' Expression )* ')'
|
[21] |
Negation |
::= | 'NOT' 'DATA'? '{' BodyBasic '}'
|
[22] |
BodyBasic |
::= | BodyTriplesBlock? ( BodyBasicNotTriples '.'? BodyTriplesBlock? )*
|
[23] |
BodyBasicNotTriples
|
::= | Filter |
[24] |
Assignment |
::= | 'SET' '(' Var ':=' Expression ')'
|
[25] |
DataTriplesBlock
|
::= | TriplesSameSubjectData ( '.' DataTriplesBlock? )?
|
[26] |
TriplesSameSubjectData
|
::= | RDFTermData PropertyListNotEmptyData | TriplesNodeData PropertyListData | ReifiedTripleBlockData
|
[27] |
PropertyListData
|
::= | PropertyListNotEmptyData?
|
[28] |
PropertyListNotEmptyData
|
::= | VerbData ObjectListData ( ';' ( VerbData ObjectListData )? )*
|
[29] |
VerbData |
::= | iri | 'a'
|
[30] |
ObjectListData |
::= | ObjectData ( ',' ObjectData )*
|
[31] |
ObjectData |
::= | GraphNodeData AnnotationData
|
[32] |
GraphNodeData |
::= | RDFTermData | TriplesNodeData | ReifiedTripleData
|
[33] |
TriplesNodeData |
::= | CollectionData | BlankNodePropertyListData
|
[34] |
BlankNodePropertyListData
|
::= | '[' PropertyListNotEmptyData ']'
|
[35] |
CollectionData |
::= | '(' GraphNodeData+ ')'
|
[36] |
AnnotationData |
::= | ( ReifierData | AnnotationBlockData )*
|
[37] |
AnnotationBlockData
|
::= | '{|' PropertyListNotEmptyData '|}'
|
[38] |
ReifierData |
::= | '~' ReifierIdData?
|
[39] |
ReifierIdData |
::= | iri | BlankNode
|
[40] |
ReifiedTripleBlockData
|
::= | ReifiedTripleData PropertyListData
|
[41] |
ReifiedTripleData
|
::= | '<<' ReifiedTripleSubjectData VerbData ReifiedTripleObjectData ReifierData? '>>'
|
[42] |
ReifiedTripleSubjectData
|
::= | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | ReifiedTripleData | TripleTermData
|
[43] |
ReifiedTripleObjectData
|
::= | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | ReifiedTripleData | TripleTermData
|
[44] |
TripleTermData |
::= | '<<(' TripleTermSubjectData VerbData TripleTermObjectData ')>>'
|
[45] |
TripleTermSubjectData
|
::= | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | TripleTermData
|
[46] |
TripleTermObjectData
|
::= | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | TripleTermData
|
[47] |
HeadTemplateBlock
|
::= | TriplesBlockTemplate
|
[48] |
TriplesBlockTemplate
|
::= | TriplesSameSubjectTemplate ( '.' TriplesBlockTemplate? )?
|
[49] |
TriplesSameSubjectTemplate
|
::= | VarOrRDFTerm PropertyListNotEmptyTemplate | TriplesNodeTemplate PropertyListTemplate | ReifiedTripleBlockTemplate
|
[50] |
PropertyListTemplate
|
::= | PropertyListNotEmptyTemplate?
|
[51] |
PropertyListNotEmptyTemplate
|
::= | Verb ObjectListTemplate ( ';' ( Verb ObjectListTemplate )? )*
|
[52] |
ObjectListTemplate
|
::= | ObjectTemplate ( ',' ObjectTemplate )*
|
[53] |
ObjectTemplate |
::= | GraphNodeTemplate AnnotationTemplate
|
[54] |
GraphNodeTemplate
|
::= | VarOrRDFTerm | TriplesNodeTemplate | ReifiedTriple
|
[55] |
TriplesNodeTemplate
|
::= | CollectionTemplate | BlankNodePropertyListTemplate
|
[56] |
BlankNodePropertyListTemplate
|
::= | '[' PropertyListNotEmptyTemplate ']'
|
[57] |
CollectionTemplate
|
::= | '(' GraphNodeTemplate+ ')'
|
[58] |
AnnotationTemplate
|
::= | ( Reifier | AnnotationBlockTemplate )*
|
[59] |
AnnotationBlockTemplate
|
::= | '{|' PropertyListNotEmptyTemplate '|}'
|
[60] |
ReifiedTripleBlockTemplate
|
::= | ReifiedTriple PropertyListTemplate
|
[61] |
BodyTriplesBlock
|
::= | TriplesBlockPattern
|
[62] |
TriplesBlockPattern
|
::= | TriplesSameSubjectPattern ( '.' TriplesBlockPattern? )?
|
[63] |
ReifiedTripleBlockPattern
|
::= | ReifiedTriple PropertyListPattern
|
[64] |
TriplesSameSubjectPattern
|
::= | VarOrRDFTerm PropertyListNotEmptyPattern | TriplesNodePattern PropertyListPattern | ReifiedTripleBlockPattern
|
[65] |
PropertyListPattern
|
::= | PropertyListNotEmptyPattern?
|
[66] |
PropertyListNotEmptyPattern
|
::= | ( VerbPath | Var ) ObjectListPattern ( ';' ( ( VerbPath | Var ) ObjectListPattern )? )*
|
[67] |
ObjectListPattern
|
::= | ObjectPattern ( ',' ObjectPattern )*
|
[68] |
ObjectPattern |
::= | GraphNodePattern AnnotationPattern
|
[69] |
TriplesNodePattern
|
::= | CollectionPattern | BlankNodePropertyListPattern
|
[70] |
BlankNodePropertyListPattern
|
::= | '[' PropertyListNotEmptyPattern ']'
|
[71] |
CollectionPattern
|
::= | '(' GraphNodePattern+ ')'
|
[72] |
AnnotationPattern
|
::= | ( Reifier | AnnotationBlockPattern )*
|
[73] |
AnnotationBlockPattern
|
::= | '{|' PropertyListNotEmptyPattern '|}'
|
[74] |
GraphNodePattern
|
::= | VarOrRDFTerm | TriplesNodePattern | ReifiedTriple
|
[75] |
Reifier |
::= | '~' ReifierId?
|
[76] |
ReifierId |
::= | Var | iri | BlankNode
|
[77] |
ReifiedTriple |
::= | '<<' ReifiedTripleSubject Verb ReifiedTripleObject Reifier? '>>'
|
[78] |
ReifiedTripleSubject
|
::= | Var | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | ReifiedTriple | TripleTerm
|
[79] |
ReifiedTripleObject
|
::= | Var | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | ReifiedTriple | TripleTerm
|
[80] |
TripleTerm |
::= | '<<(' TripleTermSubject Verb TripleTermObject ')>>'
|
[81] |
TripleTermSubject
|
::= | Var | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | TripleTerm
|
[82] |
TripleTermObject
|
::= | Var | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | TripleTerm
|
[83] |
Verb |
::= | VarOrIri | 'a'
|
[84] |
VerbPath |
::= | Path |
[85] |
Path |
::= | PathSequence |
[86] |
PathSequence |
::= | PathEltOrInverse ( '/' PathEltOrInverse )*
|
[87] |
PathEltOrInverse
|
::= | PathElt | '^' PathElt
|
[88] |
PathElt |
::= | ( iri | 'a' | '(' Path ')' )
|
[89] |
RDFTermData |
::= | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | NIL | TripleTermData
|
[90] |
VarOrRDFTerm |
::= | Var | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | NIL | TripleTerm
|
[91] |
VarOrIri |
::= | Var | iri
|
[92] |
Var |
::= | VAR1 | VAR2
|
[93] |
RDFLiteral |
::= | String ( LANG_DIR | '^^' iri )?
|
[94] |
NumericLiteral |
::= | NumericLiteralUnsigned | NumericLiteralPositive | NumericLiteralNegative
|
[95] |
NumericLiteralUnsigned
|
::= | INTEGER | DECIMAL | DOUBLE
|
[96] |
NumericLiteralPositive
|
::= | INTEGER_POSITIVE | DECIMAL_POSITIVE | DOUBLE_POSITIVE
|
[97] |
NumericLiteralNegative
|
::= | INTEGER_NEGATIVE | DECIMAL_NEGATIVE | DOUBLE_NEGATIVE
|
[98] |
BooleanLiteral |
::= | 'true' | 'false'
|
[99] |
String |
::= | STRING_LITERAL1 | STRING_LITERAL2 | STRING_LITERAL_LONG1 | STRING_LITERAL_LONG2
|
[100] |
iri |
::= | IRIREF | PrefixedName
|
[101] |
PrefixedName |
::= | PNAME_LN | PNAME_NS
|
[102] |
BlankNode |
::= | BLANK_NODE_LABEL | ANON
|
[103] |
Expression |
::= | ConditionalOrExpression
|
[104] |
ConditionalOrExpression
|
::= | ConditionalAndExpression ( '||' ConditionalAndExpression )*
|
[105] |
ConditionalAndExpression
|
::= | ValueLogical ( '&&' ValueLogical )*
|
[106] |
ValueLogical |
::= | RelationalExpression
|
[107] |
RelationalExpression
|
::= | NumericExpression ( '=' NumericExpression | '!=' NumericExpression | '<' NumericExpression | '>' NumericExpression | '<=' NumericExpression | '>=' NumericExpression | 'IN' ExpressionList | 'NOT' 'IN' ExpressionList )?
|
[108] |
NumericExpression
|
::= | AdditiveExpression
|
[109] |
AdditiveExpression
|
::= | MultiplicativeExpression ( '+' MultiplicativeExpression | '-' MultiplicativeExpression | ( NumericLiteralPositive | NumericLiteralNegative ) ( ( '*' UnaryExpression ) | ( '/' UnaryExpression ) )* )*
|
[110] |
MultiplicativeExpression
|
::= | UnaryExpression ( '*' UnaryExpression | '/' UnaryExpression )*
|
[111] |
UnaryExpression |
::= | '!' PrimaryExpression
|
[112] |
PrimaryExpression
|
::= | BrackettedExpression | BuiltInCall | iriOrFunction | RDFLiteral | NumericLiteral | BooleanLiteral | Var | ExprTripleTerm
|
[113] |
iriOrFunction |
::= | iri ArgList?
|
[114] |
ExprTripleTerm |
::= | '<<(' ExprTripleTermSubject Verb ExprTripleTermObject ')>>'
|
[115] |
ExprTripleTermSubject
|
::= | iri | RDFLiteral | NumericLiteral | BooleanLiteral | Var
|
[116] |
ExprTripleTermObject
|
::= | iri | RDFLiteral | NumericLiteral | BooleanLiteral | Var | ExprTripleTerm
|
[117] |
BrackettedExpression
|
::= | '(' Expression ')'
|
[118] |
BuiltInCall |
::= | 'STR' '(' Expression ')'
|
终结符的产生式:
[119] |
IRIREF |
::= | '<' ([^<>"{}|^`\]-[#x00-#x20] | UCHAR )* '>'
|
[120] |
PNAME_NS |
::= | PN_PREFIX? ':' |
[121] |
PNAME_LN |
::= | PNAME_NS PN_LOCAL
|
[122] |
BLANK_NODE_LABEL
|
::= | '_:' ( PN_CHARS_U | [0-9] ) ((PN_CHARS|'.')* PN_CHARS)?
|
[123] |
VAR1 |
::= | '?' VARNAME |
[124] |
VAR2 |
::= | '$' VARNAME |
[125] |
LANG_DIR |
::= | '@' [a-zA-Z]+ ('-' [a-zA-Z0-9]+)* ('--' [a-zA-Z]+)?
|
[126] |
INTEGER |
::= | [0-9]+ |
[127] |
DECIMAL |
::= | [0-9]* '.' [0-9]+ |
[128] |
DOUBLE |
::= | ( ([0-9]+ ('.'[0-9]*)? ) | ( '.' ([0-9])+ ) ) [eE][+-]?[0-9]+
|
[129] |
INTEGER_POSITIVE
|
::= | '+' INTEGER
|
[130] |
DECIMAL_POSITIVE
|
::= | '+' DECIMAL
|
[131] |
DOUBLE_POSITIVE |
::= | '+' DOUBLE
|
[132] |
INTEGER_NEGATIVE
|
::= | '-' INTEGER
|
[133] |
DECIMAL_NEGATIVE
|
::= | '-' DECIMAL
|
[134] |
DOUBLE_NEGATIVE |
::= | '-' DOUBLE
|
[135] |
STRING_LITERAL1 |
::= | "'" ( ([^#x27#x5C#xA#xD]) | ECHAR | UCHAR )* "'"
|
[136] |
STRING_LITERAL2 |
::= | '"' ( ([^#x22#x5C#xA#xD]) | ECHAR | UCHAR )* '"'
|
[137] |
STRING_LITERAL_LONG1
|
::= | "'''" ( ( "'" | "''" )? ( [^'\] | ECHAR | UCHAR ) )* "'''"
|
[138] |
STRING_LITERAL_LONG2
|
::= | '"""' ( ( '"' | '""' )? ( [^"\] | ECHAR | UCHAR ) )* '"""'
|
[139] |
ECHAR |
::= | '\' [tbnrf\"'] |
[140] |
UCHAR |
::= | ('\u' HEX HEX HEX HEX) | ('\U' HEX HEX HEX HEX HEX HEX HEX HEX)
|
[141] |
NIL |
::= | '(' WS* ')' |
[142] |
WS |
::= | #x20 | #x9 | #xD | #xA |
[143] |
ANON |
::= | '[' WS* ']' |
[144] |
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]
|
[145] |
PN_CHARS_U |
::= | PN_CHARS_BASE | '_'
|
[146] |
VARNAME |
::= | ( PN_CHARS_U | [0-9] ) ( PN_CHARS_U | [0-9] | #x00B7 | [#x0300-#x036F] | [#x203F-#x2040] )*
|
[147] |
PN_CHARS |
::= | PN_CHARS_U | '-' | [0-9] | #x00B7 | [#x0300-#x036F] | [#x203F-#x2040]
|
[148] |
PN_PREFIX |
::= | PN_CHARS_BASE ((PN_CHARS|'.')* PN_CHARS)?
|
[149] |
PN_LOCAL |
::= | (PN_CHARS_U | ':' | [0-9] | PLX ) ((PN_CHARS | '.' | ':' | PLX)* (PN_CHARS | ':' | PLX) )?
|
[150] |
PLX |
::= | PERCENT | PN_LOCAL_ESC
|
[151] |
PERCENT |
::= | '%' HEX HEX
|
[152] |
HEX |
::= | [0-9] | [A-F] | [a-f] |
[153] |
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
合并
操作。
SPARQL-RL 的互联网媒体类型(以前称为 MIME 类型)是
“application/sparql-rl”。
以下信息已提交给互联网工程指导组 (IESG),以供审查、批准并在 IANA 注册。
versionversion 的可接受值在
版本标签中定义。
profileprofile 参数的值是一个以空格分隔的非空 URI 列表。
有关更多信息和背景,请参阅 [RFC6906]。
本节是非规范性的。
SPARQL-RL 文档可能包含引用其他
SPARQL-RL 文档并将其内容导入当前
文档的 IMPORTS 语句。
如果导入的文档本身包含自己的 IMPORTS 语句,
则也会导入这些文档。
虽然使用 IMPORTS 语句有助于将规则集模块化,但也可能
引入安全风险。
风险包括但不限于:规则集
在求值时恶意或意外地导致过量计算;
HTTP 请求被拦截并返回不同的文档,
包括过期的副本。
将 SPARQL-RL 规则集应用于数据图可能会导致 大量计算和内存使用,这可能被利用 来造成拒绝服务。 应用程序应该谨慎限制应用 SPARQL-RL 规则集可能导致的 计算量和内存使用量。
SPARQL-RL 语法使用 UTF-8 [RFC3629] 编码,并 允许 在字符串数据中使用未转义的控制字符。 尽管本规范不会直接向最终用户公开这些内容, 但这些内容可能会通过用户代理呈现,而呈现此类字符 可能导致所显示的文本被混淆。
SPARQL-RL 可用于处理和创建任意应用程序数据; 安全注意事项会因使用领域而异。 适用于文本的安全工具和协议 (例如 PGP 加密、校验和验证、密码保护的压缩) 也可用于SPARQL-RL 文档。 必须实施能够反映 SPARQL-RL 规则集求值结果中 信息敏感性的安全/隐私协议。
SPARQL-RL 的安全注意事项包括 RDF 数据以及 RDF Turtle等格式的安全注意事项。
本节是非规范性的。
SPARQL-RL 文档可以包含附加的应用程序数据, 其中可能包括个人身份信息(PII) 或其他可能被视为敏感的信息。 建议发布包含此类信息的规则集的作者仔细 考虑发布此类信息的必要性和用途, 以及预期使用并可能披露这些数据的地区 所适用的法规(例如 GDPR、 CCPA、 其他法规), 尤其应考虑访问这些数据是否需要授权措施。
本节是非规范性的。
以下人员为数据形状工作组规则任务组中的 SPARQL-RL 开发工作作出了贡献: Robert David、David Habgood、Livio Robaldo、Ognjen Savkovic、Simon Steyskal、Ted Thibodeau Jr 和 Andy Seaborne。
数据形状工作组的成员包括 @@。
本规范中未列出任何议题。
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: