SHACL 1.2 规则

W3C 工作草案

关于本文档的更多详细信息
此版本:
https://www.w3.org/TR/2026/WD-shacl12-rules-20260703/
最新发布版本:
https://www.w3.org/TR/shacl12-rules/
最新编辑草案:
https://w3c.github.io/data-shapes/shacl12-rules/
历史:
https://www.w3.org/standards/history/shacl12-rules/
提交历史
编辑:
Robert David (Ontotext)
(KurrawongAI)
Andy Seaborne (Apache 软件 基金会)
Simon Steyskal (Siemens AG)
反馈:
GitHub w3c/data-shapes (拉取请求, 新建议题, 开放议题)

摘要

本文档定义了 SHACL 规则

SHACL,即形状约束语言,是一种用于描述 RDF 图结构的语言。 SHACL 可用于多种目的,例如验证、推理、 领域建模、生成本体以告知其他代理、构建用户 界面、生成代码以及集成数据。

SHACL 规则通过根据一组规则与一个基础数据图的组合生成新的 RDF 数据, 来提供推理能力。规则可以表示为 RDF, 或表示为形状规则语言(SRL)。

本规范由 数据形状工作组发布。

本文档状态

本节描述本文档在发布时的状态。 当前 W3C 出版物列表以及本技术报告的最新修订版本可在 W3C 标准与草案 索引中找到。

本文档由 数据形状工作 组作为 工作草案发布,并使用 推荐标准 轨道

作为 工作草案发布并不意味着 W3C 及其成员的认可。

这是一份草案文档,可能会随时被其他文档 更新、取代或废弃。除作为 进行中的工作之外,不宜引用本文档。 对这一即将成为推荐标准的文档的未来更新可能会纳入 新特性

本文档由一个依据 W3C 专利 政策运作的工作组 生成。 W3C 维护一份 与该组交付成果相关的任何专利 披露的公开列表; 该页面还包括 披露专利的说明。实际 知晓某项专利且认为该专利包含 必要权利要求 的个人,必须根据 W3C 专利政策第 6 节披露该信息。

本文档受 2025 年 8 月 18 日 W3C 流程文档管辖。

SHACL 规范

本规范是 SHACL 1.2 规范系列的一部分。有关这些规范的更详细 介绍,请参见 SHACL 1.2 概述

这些规范如下:

工作草案:

SHACL 1.2 Core
定义 SHACL 的核心
SHACL 1.2 SPARQL Extensions
定义 SHACL 中与 SPARQL 相关的扩展
SHACL 1.2 Node Expressions
定义用于确定 SHACL 中焦点节点的图表达式
SHACL 1.2 Rules
定义 SHACL 基于规则的推理方法
SHACL 1.2 UI
定义 SHACL 用于生成用户界面的用途
SHACL 1.2 Profiling
定义 SHACL 用于分析数据(包括 SHACL 数据)的用途

工作组说明草案:

SHACL 1.2 Overview
概述 SHACL 规范集合
SHACL 1.2 Compact Syntax
定义一种用于表达 SHACL 概念的 RDF 语法

实现者可以通过成功通过 SHACL 1.2 测试套件 的测试用例,部分检查其与上述规范的一致性级别。 但是请注意,通过测试套件中的所有测试并不意味着完全符合这些 规范。 它仅意味着该实现符合测试套件所测试的方面。

1. 引言

本文档介绍 SHACL 1.2 的推理规则,这是一种通过声明式规则 从现有 RDF 数据中派生新的 RDF 三元组的机制。 本文档定义基于规则的推理的语法和语义。

SHACL 规则的实现提供两种操作。 infer 操作将规则应用于给定的 基础图,并生成一个包含通过 执行规则派生出的 RDF 三元组的推理图。 是否将推理图与基础图合并是可选的,并 留给用户决定。query 操作用于确定某个 给定目标模式是否可以使用这些规则从基础图派生出来。

SHACL 规则允许使用新的 RDF 术语,包括空白节点, 这些术语可用于规则头部的三元组模板。

SHACL 规则还支持一些构造,例如失败即否定, 这些构造可能会因规则执行顺序不同而导致不同的推理图。 为避免这种情况,规则使用 分层技术进行求值,该技术在规则之间建立单一的隐式顺序, 确保始终生成相同的推理图。

1.1 术语

以下其他规范提供了本文档中使用的基础术语:

1.2 文档约定

本文档中的一些示例使用 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.com/

在整个文档中,将出现包含 Turtle 格式 RDF 图的彩色编码框。 这些 Turtle 文档片段使用上面给出的前缀绑定。

编辑者注

重置。在适当处使用 reSpec 默认外观。

# This box represents rules
# This box represents input data
# This box represents inferred data

2. 一致性

除标记为非规范性的章节外,本规范中的所有编写指南、图表、示例和注释均为非规范性内容。除此之外,本规范中的所有内容均为规范性内容。

本文档中的关键词 MUST NOTSHOULD 应按 BCP 14 [RFC2119] [RFC8174] 中所述进行解释,且仅当它们像此处所示那样 全部以大写形式出现时才如此解释。

本规范为以下内容定义一致性标准:

一个符合一致性的 形状规则 语言文档是一个 RDF 字符串,它 符合语法,并从 RuleSet 产生式开始,且符合 7. 形状规则语言 语法中定义的附加约束

本规范未定义 SHACL 规则 处理器如何处理不符合一致性的规则集

2.1 版本标签

版本标签是一个用于标识形状规则语言的语法和语义 一致性的字符串。

版本标签
版本标签
"1.2"

版本声明 SHOULD 在文档的早期给出。

一个形状 规则语言文档中可以出现多个版本指令。 每个指令适用于其后的文档部分, 直到遇到另一个指令或到达文档末尾。

版本标签也可以由 媒体类型version 参数给出。在没有当前 版本指令的情况下,会考虑作为媒体类型一部分指定的版本。

3. SHACL 规则

本节为非规范性内容。

给定一个基础图和一个规则集,SHACL 规则会推断新的三元组。 求值的输出是一个推理图,其中包含派生出的、 不出现在基础图中的三元组。

每个规则都有一个称为主体的模式,以及一个 称为头部的结果模板。 规则通过查找主体中变量的值来执行, 使主体匹配组合后的基础图以及 到目前为止执行过程中产生的任何推理三元组。 然后使用这些值来实例化规则头部中的 三元组模板,以生成新的推理三元组。

规则会一直执行,直到不再推断出更多三元组; 随着新的推理三元组可用,规则可能会被执行多次。

SHACL 规则执行被定义为:在创建新的 RDF 术语时, 包括新的空白节点,以及在测试某个模式是否不存在时, 规则执行顺序不会导致不同结果。换言之,无论规则执行顺序如何, 都会生成相同的推理图。

SHACL 规则既有 RDF 语法,也有受 SPARQL 1.2 Query Language 启发的人类友好语法。 规则集求值包含与 SPARQL 类似的元素,但在细节上有所不同, 以确保无论规则执行顺序如何,都生成相同的推理图。

3.1 基本模式

在第一个示例中,我们有以下数据图和规则集:

上述规则应用到数据后,将得出结论: :X:A:B:childOf

然后我们可以通过添加一个依赖于其他规则产生的 :childOf 三元组的规则,派生 :descendedFrom 关系:

3.2 递归

我们可以添加一个依赖于 :descendedFrom 三元组的规则, 以推断 :X:C:descendedFrom

这会添加三元组 :X :descendedFrom :C

最后一条规则是一个递归规则,其规则主体 依赖于该规则的头部。

3.3 过滤

我们可以在规则主体中使用表达式来限制主体匹配中 变量的取值。 例如,给定关于城镇及其人口的数据, 我们可以为人口大于 1500 的城镇推断一个类:

FILTER 对表达式求值;如果表达式求值为 true, 则保留当前的变量绑定集合;如果表达式求值为 false,则丢弃 当前的变量绑定集合。 这与 SPARQL 的 FILTER 操作 相同,并且 SHACL 规则提供了许多与 SPARQL 相同的函数和运算符。

3.4 否定

否定允许你指定一个不得匹配的模式。 这称为“失败即否定”。

为了对否定元素求值,规则求值算法会确保 所有可能产生与否定元素中的模式匹配的三元组的规则 都已完成。这称为 分层,并确保否定基于 所有相关的可能三元组,无论这些三元组来自数据, 还是来自其他规则推断出的推理三元组。

3.5 赋值和创建 RDF 术语

赋值允许你将表达式的结果赋给规则主体中的一个变量。 这可用于基于数据创建新的 RDF 术语。

空白节点可以用于规则头部,并且每次规则求值生成三元组时, 每个空白节点都会生成一个新的空白节点。

涉及赋值的规则,以及在其 规则头部中创建空白节点的规则,都是只运行一次规则。 这类规则会在所有可能产生其所依赖数据的规则之后运行, 并在任何依赖其产生数据的规则之前运行。 在规则头部中涉及空白节点的规则也会创建新的 RDF 术语,并且是只运行一次规则。

此条件确保创建 RDF 术语的规则不会多次生成新术语, 从而导致潜在的不同结果;同时也确保此类规则不会 回到自身并造成无限数量的 RDF 术语。

如果对赋值中的表达式求值导致错误, 则当前解映射会被该赋值拒绝。

3.6 导入规则

一个 SHACL 规则集可以通过在该规则集的 规则导入中 包含其他规则集的 URL 来纳入其他规则集。 这允许将规则组织成可在规则集之间共享的库。

规则集中的 IMPORTS 语句会在该规则集中任何规则 被求值之前处理。在导入步骤期间,如果被导入的规则集 有自己的导入项,这些导入项也会被递归处理。 在处理规则集时遍历 IMPORTS 语句可能导致 循环导入。一个规则集只会被导入一次; 导入语句图中的循环不会导致无限循环。

3.7 数据块

数据块允许以简洁方式直接向规则集求值提供 RDF 三元组。 数据块中的三元组会被添加到推理图中,并可用于 规则主体中的匹配。

@@示例待更新

3.8 规则集求值

规则被组织为规则集。 规则集和数据图(即基础图)是求值的输入。 输出是一个称为推理图的图, 其中推理出的三元组不出现在数据图中。

在求值期间,根据某条规则推理出的三元组 可用于其他规则中的匹配。 规则集求值会持续进行,直到推理图 包含从规则集和数据图的输入中 可得到的所有可能三元组。

在规则本身求值之前,求值会从两个步骤开始, 以准备规则集

  1. 解析输入规则集的导入项,以形成一个 包含所有被导入规则集的所有规则的 规则集。 (6.2.1 处理 导入)。
  2. 计算一个分层4.4.2 分层 算法), 以使否定元素赋值元素产生一致、 可预测的结果。分层涉及检查 规则之间的依赖关系 (4.3.1 依赖 图)。

一旦规则集完成准备,求值就会 按顺序取出分层中的每一层, 将该层中的规则求值至完成, 然后进入下一层。

3.9 SHACL 规则与 SPARQL 之间的关系

本节为非规范性内容。

SHACL 规则与 SPARQL 关系密切。SHACL 规则被设计为与 SPARQL 兼容,并且 SHACL 规则中的许多构造都受到 SPARQL 的启发。 但是,也存在一些差异:

3.10 规则元组

议题 752:规则元组 - 规则求值期间的工作空间 Rules

有风险:

规则元组与三元组不相交。 它们是 RDF 术语(无变量)的元组,并且仅在 规则集求值期间存在。 它们可用于记录规则求值期间的中间结果, 并在规则之间传递数据。

元组模式、模板和元组的语法:

  • TUPLE(termOrVar , ...)
  • 简写:$(termOrVar , ...)

通常,第一个参数会是一个固定名称。

存在一个元组存储,它在求值的整个生命周期内保存元组。 该元组存储会保存重复的数据元组(不同于 作为集合的 RDF 图)。

4. 形状规则抽象语法

形状规则抽象语法是 SHACL 规则的逻辑结构。 它用于定义 SHACL 规则的执行算法。 SHACL 规则的两种具体语法形式,即形状规则语言(SRL) 和 RDF 语法(SRL/RDF),都提供了一种表达抽象语法的方式。

4.1 抽象语法的 元素

变量
变量表示三元组模式中一个可能的 RDF 术语。 变量也用于表达式中。
表达式
表达式是函数或函数形式; 其参数是 RDF 术语。 表达式相对于一个解映射求值,并给出一个 RDF 术语作为结果。 表达式SHACL 列表参数函数 以及 SPARQL 表达式兼容。
数据块
数据块是一组三元组。 这些三元组会作为附加事实添加到推理图中,并 包含在推理过程中。
三元组模板
三元组模板是一个 3 元组,其中每个元素要么是 变量,要么是一个 RDF 术语(它可能是一个三元组术语)。 该元组的第二个元素必须是 IRI变量三元组模板出现在规则头部中。
三元组 模式
三元组模式是一个 3 元组,其中每个元素要么是 变量,要么是一个 RDF 术语(它可能是一个三元组术语)。 该元组的第二个元素必须是 IRI变量
过滤元素
过滤元素是作为规则元素 出现的表达式。 它用于在模式匹配中限制变量的取值。
三元组模式元素
三元组模式元素是作为规则元素出现的三元组模式
否定元素
否定元素是一个规则元素。 它有一个否定元素主体, 由一系列三元组模式元素过滤元素组成。
赋值元素
赋值元素是一个规则元素,它是 由一个称为赋值变量变量, 以及一个称为赋值表达式表达式组成的一对。
规则元素
规则元素是以下任一项: 三元组模式元素过滤元素否定元素,或 赋值元素
规则头部
规则头部是一系列三元组 模板
规则主体
规则主体是一系列规则元素; 也就是说,每个序列元素都是以下之一: 一个三元组模式元素、 一个过滤元素、 一个否定元素, 或一个赋值元素
规则导入
规则导入(通常简称为“导入”)是一组 URL, 这些 URL 指向将在求值期间被包含的其他规则集。
规则
规则是一对元素:一个规则头部(通常简称为 “头部”)和 一个规则主体(通常简称为“主体”)。 规则可以被赋予一个 URI 以帮助识别它。
只运行一次规则
只运行一次规则是在规则集求值中的某个特定点 恰好运行一次的规则。
一般规则
一般规则是不是只运行一次规则的规则。 一般规则在规则集求值期间可以运行多次。
规则集
规则集是零个或多个规则的集合、 零个或多个数据块的集合, 以及零个或多个规则导入的集合。 已解析 规则集是一个没有导入项的规则集已解析规则集通过将规则集 应用导入过程来从另一个规则集创建。
基础图
基础图是作为 求值过程输入给出的 RDF 图
推理 图
推理图是通过对 规则集求值而产生的 RDF 图。它包含所有不在基础图中、但通过将规则集应用于 基础图而推断出的三元组。
Infer
Infer 是将规则集应用于给定 基础图,并产生包含推理 三元组的推理 图的操作。
Query
Query 是确定某个给定目标 模式是否可以使用 规则集基础图派生出来的操作。

三元组模式三元组 模板中, 元组的位置 1 非正式地称为主语, 位置 2 非正式地称为谓语,而 位置 3 非正式地称为宾语

一系列规则元素中的元素从 1 开始编号。

4.2 良构性条件

良构性是一组作用于 规则集抽象语法的条件。这些条件共同确保 规则头部中的 变量 在规则主体中有已定义的值; 过滤元素赋值表达式中的每个变量 在求值点都有值; 并且规则中的每个 赋值都会引入一个新的变量, 即该变量此前未在规则主体中使用过。

我们为给定初始变量集合的一系列规则元素 定义良构性

elti 为一系列规则元素 中的第 i 个元素。

varsi 为由 elti 定义的变量集合,其中:

V0 为一个序列的初始变量。

ViV0 与所有 varsj 的并集, 其中 j 小于 i

VallVN, 其中 N 是该序列的长度。

良构 序列是在给定变量集合 V0 的情况下, 满足以下条件的一系列规则元素

一个规则是一个良构 规则, 如果其规则主体序列在给定 V0 为空集合的情况下是一个良构序列, 并且规则头部三元组模板中的每个变量 都是 Vall 的元素。

一个规则集是一个良构规则集,当且仅当 该规则集的所有规则都是良构规则

4.3 规则依赖

如果第二条规则的输出会影响第一条规则主体的求值,则规则 R1 依赖于规则 R2。也就是说,R2 的头部 有一个三元组模板,该模板可能生成一个三元组,与 R1 主体中的三元组模式相匹配, 无论它是作为三元组模式元素出现,还是出现在 否定元素内部。

有两种依赖: 封闭依赖开放 依赖封闭依赖确保规则 R2 在规则 R1 执行之前已生成其所有可能输出。 如果规则依赖不是封闭的,则它是开放依赖, 这允许第一条规则 R1 在规则 R2 仍可能 再次运行以生成更多三元组时执行, 这些来自 R2 的新三元组随后可导致 R1 重新求值。

在第一个示例中,第一条规则对第二条规则具有开放依赖。

在第二个示例中,第一条规则对第二条规则具有封闭依赖。

三元组 模式匹配

如果一个三元组模板能够生成一个与 三元组模式匹配的三元组, 则该三元组模式匹配该三元组模板。

三元组模式依赖

如果一个三元组模板可能被某个 三元组模式匹配,则该 三元组模式 依赖于该三元组模板

如果三元组模式对规则的 头部中的任何三元组模板有依赖, 则该三元组模式 依赖于该规则

规则依赖

如果 R1 主体中的任何 三元组模式, 无论作为三元组模式元素还是 出现在否定元素内部, 依赖 于 R2 头部中的三元组模板, 则规则 R1 依赖于 R2

封闭依赖

如果满足以下任一条件,则规则 R1 对规则 R2规则依赖封闭依赖

开放依赖

如果规则 R1 对规则 R2规则依赖不是 封闭依赖, 则它是开放依赖。 也就是说,R1 中依赖于 R2 的任何三元组模式都只作为 三元组模式元素出现。

具有组件 tSubjtPredtObj三元组模板, 如果以下所有条件均为真,则可能生成一个具有组件 RDF 术语 spo 的三元组:

此外,如果 tSubjtPredtObj 中的任意一对 是同一个变量, 那么 spo 中对应的一对也必须相同。

4.3.1 依赖图

规则之间的依赖关系表示为一个有向图, 称为依赖图。 图的顶点是规则集中的规则,边 根据该依赖是开放依赖还是 封闭依赖, 被标记为 openclosed

依赖图
依赖图是一个规则集的有向图,其中每个 顶点都是规则集中的一个规则,并且如果规则 R1 依赖于规则 R2,则存在一条从规则 R1 到规则 R2 的边。 该边根据该依赖是开放依赖还是 封闭依赖, 被标记为 openclosed
传递规则依赖
如果在依赖图中存在从 规则 R1 到规则 R2 的路径, 则规则 R1 对规则 R2 具有传递依赖
递归规则依赖

如果在依赖图中存在一个涉及 R 的 循环路径, 则规则 R 具有递归依赖

依赖图不受数据图影响。

4.3.2 依赖图算法

以下算法给出一种 从规则集构造 依赖 图的可能方法。一致性取决于产生一个符合依赖图定义的依赖图, 而不是取决于是否使用此过程。

define mergeLabel(oldLabel, newLabel):
    ## Closed dependency overrides open dependency.
    if oldLabel == "open" and newLabel == "open":
        return "open"
    else:
        return "closed"
    endif
enddefine

## output -- Dependency graph with rule vertices and labeled edges.
define buildDependencyGraph(ruleSet):
    ## edgeLabelMap maps (R1, R2) to "open" or "closed"
    let edgeLabelMap be a map from pair (rule, rule) to label

    foreach rule R1 in ruleSet:
        ## Classify each triple pattern TP in the rule as requiring "open" or "closed"
        ## depending on whether it is in a negation element or not.
        let bodyDependencies = {}
        foreach rule element RBE in the body of R1:
            if RBE is a negation element:
                foreach triple pattern TP in RBE:
                    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:
                ## Do nothing
            else if RBE is an assignment element:
                ## Do nothing
            endif
        endfor

        foreach pair (triple pattern TP, depLabel) in bodyDependencies:
            if R1 has an assignment element:
              set depLabel to "closed"
            endif
            if R1 has a triple template with a blank node:
              set depLabel to "closed"
            endif
            ## Find depenencies for this triple pattern element or negation element.
            foreach rule R2 in ruleSet:
                foreach triple template TT in head of R2:
                    ## "possibly generate" / matching is defined in
                    ## section 3.3 
                    if TT can possibly match 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:
        add edge (R1 -> R2) labeled label to DP
    endfor

    the result is DP
    enddefine

示例:

4.4 分层

分层是将规则集 划分为一个有序的分层层级序列的过程(也称为 “strata”,单数为 “stratum”)。较低分层中的规则 会先于较高分层中的规则求值。

分层规则之间的依赖施加约束, 以确保否定元素赋值 元素,以及在规则头部中创建的空白节点, 仅依赖于使用更早(更低)分层基础图计算出的结果。 这保证了对给定基础图上的规则集求值会得到单一、明确定义且有限的 结果。

分层过程也可以用于作出其他求值 决策。本文档描述了一致求值所需的条件, 并给出一种可能的分层形成方式。 实现需要满足此处描述的条件 才能获得兼容行为,但并不要求 按照所呈现的算法实现。

分层层级

分层层级 SL 是一对 互不相交的规则集合 (SL.onceSL.general)。 SL.once 包含只运行一次规则,即 使用赋值元素或 在规则头部中产生空白节点的规则; 这些规则会在该分层层级求值开始时各自恰好 求值一次。 SL.general 包含其余规则,这些规则会被反复求值, 直到不再推断出新的三元组。

分层
分层是一个规则集分层层级序列。 规则集中的每条规则 恰好出现在某个 分层层级的某个集合中。

4.4.1 分层条件

只有在满足以下条件时,才定义 分层。 如果某个规则集不满足此条件,那么本 规范不会为此类规则集的求值结果作出定义。

分层条件
分层条件要求 不存在涉及 封闭依赖递归依赖, 该依赖出现在某个规则集依赖图中。

换言之,在依赖图的任何传递依赖循环中, 都不存在 NOT 或只运行一次规则(赋值,或涉及空白节点的规则三元组模板)。

4.4.2 分层算法

以下算法给出一种仅基于规则集的可能分层方式。

## 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.
    ## The check for unbounded stratification is a guard 
    ## 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 == "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 )
                        ## Stratification requirement violated
                        error "Stratification error"
                        endif
                    stratumMap.set(pRule, xStratum)
                    maxStratum = max(maxStratum, xStratum)
                    changed = true;
                endif
            endif
        endfor
    endwhile

    ## Initialize the result map.
    let stratumRules be a map from integer to rules.
    for i = 0 to maxStratum
        stratumRules.set(i, {})
    endfor

    ## Gather rules in stratumMap with the same level number
    for rule R in map stratumMap:
        let stratumNum = stratumMap.get(R)
        add R to stratumRules.get(stratumNum)
    endfor

    ## Partition each level into once and 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

分层条件 的一个结果是,一旦 只运行一次规则求值, 用于确定该规则结果的数据在后续求值期间将不会 改变。

5. 形状规则语言的具体语法 形式

存在两种具体语法。

形状规则语言:

RDF 规则语法:

5.1 形状规则语言语法

语法如下所示。

编辑者注

将 AST 映射到抽象语法。

5.1.1 形状规则 语言缩写

附加辅助项(简写缩写):

这些允许使用常见规则模式,也允许基础引擎中 的专用实现。

  • TRANSITIVE(uri)
  • SYMMETRIC(uri)
  • INVERSE(uri, uri)
议题 779:缩写:`TRANSITIVE`、`SYMMETRIC` 和 `INVERSE` Rules

有风险:

TRANSITIVE 同时具有实现和简洁表达方面的优势。 SYMMETRICINVERSE 的实现优势并不明确。

5.2 RDF 规则语法

词汇表:rdf-syntax-vocab.ttl
SHACL 形状:rdf-syntax-shapes.ttl

编辑者注

良构性:

  • 所有 RDF 列表都是良构的
  • 对于头部或主体元素,subject - predicate - object 中恰好有一个
  • 良构、单值、列表参数节点表达式
  • 良构抽象语法
编辑者注

描述抽象模型如何映射到三元组。

编辑者注

过程:累加器,自底向上/遍历结构。

  • 收集数据三元组
  • 映射表达式
  • 映射三元组模式
  • 映射三元组模板
  • 映射赋值
  • 映射到规则
  • 规则集

不在语法中的所有三元组都会被忽略。 不允许其他 "srl:" 谓词(??)。

@@ 插图:文本和 RDF 语法中的 SHACL 规则集:全部特性:

PREFIX :        <http://example/>

DATA { :s :p :o }
RULE { ?x :q :o } WHERE { ?x :p :o }
RULE { ?x :q :o } WHERE { ?x :p :o1 ; :p :o2 }
RULE { ?x :q :o } WHERE { ?x :p ?o . FILTER (?o < 18) }
RULE { ?x :q ?o } WHERE { ?x :p :o . SET (?o := 18) }
RULE { ?x :q ?o } WHERE { ?x :p :o . NOT { ?s :p ?o . FILTER(?o < 18) } }
PREFIX :       <http://example/>
PREFIX rdf:    <http://www.w3.org/1999/02/22-rdf-syntax-ns#>
PREFIX sh:     <http://www.w3.org/ns/shacl#>
PREFIX sparql: <http://www.w3.org/ns/sparql#>
PREFIX srl:    <http://www.w3.org/ns/shacl-rules#>

:ruleSet-1
  rdf:type srl:RuleSet;
  srl:data (
    [ srl:subject :s ; srl:predicate :p; srl:object :o ; ]
  );
  srl:rules (
    [
      rdf:type srl:Rule;
      srl:body (
        [ srl:subject [ srl:varName "x" ] ; srl:predicate :p ; srl:object :o ; ]
      ) ;
      srl:head (
        [ srl:subject [ srl:varName "x" ] ; srl:predicate :q ; srl:object :o ; ]
      )
    ]
    [
      rdf:type srl:Rule ;
      srl:body (
        [ srl:subject [ srl:varName "x" ] ; srl:predicate :p ; srl:object :o1 ; ]
        [ srl:subject [ srl:varName "x" ] ; srl:predicate :p ; srl:object :o2 ; ]
      ) ;
      srl:head (
        [ srl:subject [ srl:varName "x" ] ; srl:predicate :q ; srl:object :o ; ]
      )
    ]
    [
      rdf:type srl:Rule ;
      srl:body (
        [ srl:subject [ srl:varName "x" ] ; srl:predicate :p ; srl:object [ srl:varName "o" ] ; ]
        [
          srl:filter [
            sparql:less-than (
              [ srl:varName "o" ]
              18
            )
          ]
        ]
      ) ;
      srl:head (
        [ srl:subject [ srl:varName "x" ] ; srl:predicate :q ; srl:object :o ; ]
      )
    ]
    [
      rdf:type srl:Rule ;
      srl:body (
        [ srl:subject [ srl:varName "x" ] ; srl:predicate :p ; srl:object :o ; ]
        [
          srl:assign [
            srl:assignValue 18 ;
            srl:assignVar [ srl:varName "o" ]
          ]
        ]
      ) ;
      srl:head (
        [ srl:subject [ srl:varName "x" ] ; srl:predicate :q ; srl:object [ srl:varName "o" ] ; ]
      )
    ]
    [
      rdf:type srl:Rule ;
      srl:body (
        [ srl:subject [ srl:varName "x" ] ; srl:predicate :p ; srl:object :o ; ]
        [
          srl:not (
            [ srl:subject [ srl:varName "s" ] ;     srl:predicate :p ;     srl:object [ srl:varName "o" ] ; ]
            [
              srl:filter [
                sparql:less-than (
                  [ srl:varName "o" ]
                  18
                )
              ]
            ]
          )
        ]
      ) ;
      srl:head (
        [ srl:subject [ srl:varName "x" ] ; srl:predicate :q ; srl:object [ srl:varName "o" ] ; ]
      )
    ]
  ) .

6. 规则集求值

本节定义在给定数据上对规则集求值的结果。 它并不规定该算法作为实现方法。 实现可以使用任何能生成相同结果的算法。

输入数据图 G,称为基础图,以及规则集 RS。
输出由推理三元组组成的 RDF 图 GI

推理出的三元组不包括 基础图的三元组集合中已有的三元组。

6.1 求值定义

解映射
一个解映射 μ 是一个偏函数 μ : VT, 其中 V 是所有变量的集合, T 是所有 RDF 术语的集合。 μ 的定义域记作 dom(μ),它是 Vμ 有定义的子集。在明确表示 解映射时,我们使用术语 。 将 μ0 写作这样的解映射: dom(μ0) 是空集合。
替换函数
替换函数,或简称为替换, 是一个函数 subst(μ, 三元组模式), 它返回一个三元组模式, 其中三元组模式中每个属于 dom(μ) 的变量出现处,都被该 var解映射给出的 RDF 术语替换。 如果所得三元组模式没有变量,那么它就是一个 RDF 三元组
求值 图
求值图是一个 RDF 图,它组合了 基础图 以及规则集求值期间产生的所有三元组。
图匹配
图匹配查找将三元组模式映射到 RDF 图中三元组的方式。

G 为一个 RDF 图TP 为一个 三元组模式。 函数 graphMatch(G, TP) 返回所有可能 的集合; 这些解应用于该三元组模式时,会产生一个存在于 求值图中的三元组

G 为一个 RDF 图TP 为一个 三元组模式

graphMatch(G, TP) = { μ | subst(μ, TP) is a triple in G }
解兼容
如果两个解 S1 和 S2 在共有变量上取值一致,则它们是 兼容的

令 S1 和 S2 为解。

compatible1, μ2) = true
                      if forall v in dom1) intersection dom2)
                          μ1(v) = μ2(v)
compatible1, μ2) = false otherwise
解 序列
解序列是解的多重集合。 该序列没有定义顺序。 它等价于无序列表,并且可以包含重复项。
解合并
如果两个解兼容,则两个解的合并是这样一个解: 它将每个解中的变量映射到 其中一个解给出的 RDF 术语
μ1μ2 为解映射,S1 和 S2 为解序列。
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) }
编辑者注

说明定义域是 dom(S1) ∪︀ dom(S2)

说明两个没有共有变量的解是兼容的。

6.2 求值准备

规则集求值的第一步,是准备一个单一的、 有效的规则集。这包括收集所有被导入的规则集, 构建一个单一的组合规则集,然后为该组合规则集计算 分层。

6.2.1 处理导入

已解析规则集通过递归读取另一个 规则集的导入项中提到的所有规则集, 从该规则集生成。

一个规则集有三个组件:R.rulesR.dataR.imports。 两个规则集 RS1RS2规则 集合并 是一个规则集 MR,定义如下:

MR.rules = RS1.rules ∪︀ RS2.rules
MR.data = merge(RS1.data, RS2.data)
MR.imports = {}

其中 mergeRDF 合并 操作。

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
    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)

6.2.2 计算分层

创建已解析规则集后,计算 依赖图 (参见 4.3.2 依赖图算法), 然后计算一个 [=stratification] (参见 4.4.2 分层算法)。

6.3 表达式的求值

表达式无论是用于过滤元素还是 赋值元素,都会相对于一个 解映射求值;该解映射为表达式中的每个变量提供一个 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

函数 EBV(x) 返回一个 RDF 术语的 有效布尔值

6.4 规则的求值

规则求值,是通过从规则主体计算一个解 序列, 然后使用该解序列中的每个 解映射, 用规则头部生成三元组。

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
        endif

        if rElt is a condition element with expression F:
            SEQ1 = {}
            for each solution μ in SEQ:
                let x = evalFunction(F, μ)
                if EBV(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 μ in SEQ:
                let x = evalFunction(expr, μ)
                if x is not an error:
                    ## Add mapping V -> x to solution μ
                    let μ2 be a solution mapping μ ∪︀ { (V, x) }
                    add μ2 to SEQ1
                else
                    # Error: drop solution μ
                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 OUT = empty set
for each μ in SEQ:
    let S = {}
    for each triple template TT in H:
        let triple = subst(μ, TT)
        Add triple to S
    endfor
    OUT = OUT union S
endfor

result eval(R, G) is OUT

注意,OUT 可能包含数据图中已有的三元组。

6.5 规则集的求值

规则集的求值被定义为执行该规则集 分层中的每个 分层; 其中每个分层都会先按顺序完整执行, 然后再进入下一个分层。 对一个分层求值时,首先对该分层中的每个 只运行一次规则求值,然后反复求值该分层中的 一般规则,直到不再产生新的三元组。

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 }

# Evaluation graph.
let GE = G0 ∪︀ D

for each stratum ST in LS:
    for each rule R in ST.once:
        let X = eval(R, GE)
        let Y = { t  X | t  GE }
        GI = Y ∪︀ GI
        GE = Y ∪︀ GE
    endfor

    let finished = false
    while !finished:
        finished = true
        for each rule R in ST.general:
            let X = eval(R, GE)
            let Y = { t  X | t  GE }
            if Y is not empty:
                finished = false
                GI = Y ∪︀ GI
                GE = Y ∪︀ GE
            endif
        endfor
    endwhile
endfor
the result is GI

7. 形状规则语言语法

形状规则语言文档是一个以 UTF-8 编码的 RDF 字符串 [RFC3629]。 仅允许使用 Unicode 标量值, 范围为 U+0000U+D7FF 以及 U+E000U+10FFFF。 这排除了 代理码点, 即 U+D800U+DFFF 范围。

7.1 空白

空白 (产生式 WS)用于 分隔两个否则会被(错误地)识别为一个终结符的终结符。 下方以大写表示的规则名指示空白具有重要意义的位置; 这些规则构成了用于构造形状规则语言解析器的一种可能终结符选择。

空白在产生式 String 中具有重要意义。

7.2 注释

注释以位于 IRIREFSTRING_LITERAL1STRING_LITERAL2STRING_LITERAL_LONG1STRING_LITERAL_LONG2 之外的 # 开始, 并持续到行尾(由 LFCR 标记), 如果注释标记之后没有行尾,则持续到文件结尾。 注释被视为空白。

7.3 IRI 引用

相对 IRI 引用按照 统一资源标识符(URI):通用 语法 [RFC3986], 使用第 5.2 节中的基本算法,通过基准 IRI 进行解析。 不执行基于语法的规范化或基于方案的规范化 (RFC3986 第 6.2.2 和 6.2.3 节中所述)。 IRI 引用中额外允许的字符,按 国际化资源标识符 (IRI) [RFC3987] 第 6.5 节所述,以与 URI 引用中未保留字符相同的方式处理。

BASE 指令定义用于解析 相对 IRI 引用的基准 IRI,依据 [RFC3986] 第 5.1.1 节,“内容中嵌入的基准 URI”第 5.1.2 节,“来自封装实体的基准 URI” 定义作用域内基准 IRI 如何来自封装文档, 例如带有 xml:base 指令的 SOAP 信封,或带有 Content-Location 标头的 MIME multipart 文档。 5.1.3,来自检索 URI 的基准 URI中标识的“检索 URI”, 是特定形状规则语言文档 被检索自的 URL。 如果以上均未指定基准 URI,则使用默认 基准 URI(第 5.1.4 节,“默认基准 URI”)。 每个 BASE 指令都会相对于前一个基准 URI 设置新的作用域内基准 URI。

7.4 转义序列

形状规则文档中使用三种形式的转义:

可使用每种转义序列的上下文
数字
转义
字符串
转义
保留字符
转义
IRI, 用作 RDF 术语PREFIX, 或 BASE 声明
局部名称
字符串

%-编码序列位于 IRI 的字符范围中, 并且在局部名称中被显式允许。 它们表现为一个 % 后跟两个十六进制字符,并表示这 同一组三个字符。这些序列在处理过程中不会 被解码。 写作 <http://a.example/%66oo-bar> 的术语 指示 IRI http://a.example/%66oo-bar, 而不是 IRI http://a.example/foo-bar。 带有前缀 PREFIX ex: <http://a.example/> 的术语 ex:%66oo-bar 也指示 IRI http://a.example/%66oo-bar

7.5 语法

此处使用的 EBNF 在 XML 1.0 [EBNF-NOTATION] 中定义。

说明:

  1. 语法的入口点是 RuleSet
  2. 关键词不区分大小写,除了 'a' 区分大小写。
  3. 转义序列 UCHARECHAR 区分大小写。
  4. DATA 块中不允许出现变量。
  5. 在对输入进行词法分析并选择语法规则时,选择最长匹配。
  6. 当使用名称为大写的规则作为终结符时,形状规则语言语法是 LL(1) 和 LALR(1)。
[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' iri? 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? ( BodyBasicNotTriples '.'? BodyTriplesBlock? )*
[23]   BodyBasicNotTriples   ::=   Filter
[24]   HeadTemplateBlock   ::=   TriplesBlock
[25]   TriplesBlock   ::=   TriplesSameSubjectPath ( '.' TriplesBlock? )?
[26]   ReifiedTripleBlock   ::=   ReifiedTriple PropertyList
[27]   ReifiedTripleBlockPath   ::=   ReifiedTriple PropertyListPath
[28]   Assignment   ::=   'SET' '(' Var ':=' Expression ')'
[29]   Reifier   ::=   '~' VarOrReifierId?
[30]   VarOrReifierId   ::=   Var | iri | BlankNode
[31]   Filter   ::=   'FILTER' Constraint
[32]   Constraint   ::=   BrackettedExpression | BuiltInCall | FunctionCall
[33]   FunctionCall   ::=   iri ArgList
[34]   ArgList   ::=   NIL | '(' Expression ( ',' Expression )* ')'
[35]   ExpressionList   ::=   NIL | '(' Expression ( ',' Expression )* ')'
[36]   TriplesSameSubject   ::=   VarOrTerm PropertyListNotEmpty | TriplesNode PropertyList | ReifiedTripleBlock
[37]   PropertyList   ::=   PropertyListNotEmpty?
[38]   PropertyListNotEmpty   ::=   Verb ObjectList ( ';' ( Verb ObjectList )? )*
[39]   Verb   ::=   VarOrIri | 'a'
[40]   ObjectList   ::=   Object ( ',' Object )*
[41]   Object   ::=   GraphNode Annotation
[42]   TriplesSameSubjectPath   ::=   VarOrTerm PropertyListPathNotEmpty | TriplesNodePath PropertyListPath | ReifiedTripleBlockPath
[43]   PropertyListPath   ::=   PropertyListPathNotEmpty?
[44]   PropertyListPathNotEmpty   ::=   ( VerbPath | VerbSimple ) ObjectListPath ( ';' ( ( VerbPath | VerbSimple ) ObjectListPath )? )*
[45]   VerbPath   ::=   Path
[46]   VerbSimple   ::=   Var
[47]   ObjectListPath   ::=   ObjectPath ( ',' ObjectPath )*
[48]   ObjectPath   ::=   GraphNodePath AnnotationPath
[49]   Path   ::=   PathSequence
[50]   PathSequence   ::=   PathEltOrInverse ( '/' PathEltOrInverse )*
[51]   PathEltOrInverse   ::=   PathElt | '^' PathElt
[52]   PathElt   ::=   PathPrimary
[53]   PathPrimary   ::=   iri | 'a' | '(' Path ')'
[54]   TriplesNode   ::=   Collection | BlankNodePropertyList
[55]   BlankNodePropertyList   ::=   '[' PropertyListNotEmpty ']'
[56]   TriplesNodePath   ::=   CollectionPath | BlankNodePropertyListPath
[57]   BlankNodePropertyListPath   ::=   '[' PropertyListPathNotEmpty ']'
[58]   Collection   ::=   '(' GraphNode+ ')'
[59]   CollectionPath   ::=   '(' GraphNodePath+ ')'
[60]   AnnotationPath   ::=   ( Reifier | AnnotationBlockPath )*
[61]   AnnotationBlockPath   ::=   '{|' PropertyListPathNotEmpty '|}'
[62]   Annotation   ::=   ( Reifier | AnnotationBlock )*
[63]   AnnotationBlock   ::=   '{|' PropertyListNotEmpty '|}'
[64]   GraphNode   ::=   VarOrTerm | TriplesNode | ReifiedTriple
[65]   GraphNodePath   ::=   VarOrTerm | TriplesNodePath | ReifiedTriple
[66]   VarOrTerm   ::=   Var | RDFTerm
[67]   RDFTerm   ::=   iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | NIL | TripleTerm
[68]   ReifiedTriple   ::=   '<<' ReifiedTripleSubject Verb ReifiedTripleObject Reifier? '>>'
[69]   ReifiedTripleSubject   ::=   Var | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | ReifiedTriple | TripleTerm
[70]   ReifiedTripleObject   ::=   Var | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | ReifiedTriple | TripleTerm
[71]   TripleTerm   ::=   '<<(' TripleTermSubject Verb TripleTermObject ')>>'
[72]   TripleTermSubject   ::=   Var | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | TripleTerm
[73]   TripleTermObject   ::=   Var | iri | RDFLiteral | NumericLiteral | BooleanLiteral | BlankNode | TripleTerm
[74]   TripleTermData   ::=   '<<(' TripleTermDataSubject ( iri | 'a' ) TripleTermDataObject ')>>'
[75]   TripleTermDataSubject   ::=   iri | RDFLiteral | NumericLiteral | BooleanLiteral
[76]   TripleTermDataObject   ::=   iri | RDFLiteral | NumericLiteral | BooleanLiteral | TripleTermData
[77]   VarOrIri   ::=   Var | iri
[78]   Var   ::=   VAR1 | VAR2
[79]   Expression   ::=   ConditionalOrExpression
[80]   ConditionalOrExpression   ::=   ConditionalAndExpression ( '||' ConditionalAndExpression )*
[81]   ConditionalAndExpression   ::=   ValueLogical ( '&&' ValueLogical )*
[82]   ValueLogical   ::=   RelationalExpression
[83]   RelationalExpression   ::=   NumericExpression ( '=' NumericExpression | '!=' NumericExpression | '<' NumericExpression | '>' NumericExpression | '<=' NumericExpression | '>=' NumericExpression | 'IN' ExpressionList | 'NOT' 'IN' ExpressionList )?
[84]   NumericExpression   ::=   AdditiveExpression
[85]   AdditiveExpression   ::=   MultiplicativeExpression ( '+' MultiplicativeExpression | '-' MultiplicativeExpression | ( NumericLiteralPositive | NumericLiteralNegative ) ( ( '*' UnaryExpression ) | ( '/' UnaryExpression ) )* )*
[86]   MultiplicativeExpression   ::=   UnaryExpression ( '*' UnaryExpression | '/' UnaryExpression )*
[87]   UnaryExpression   ::=     '!' PrimaryExpression
| '+' PrimaryExpression
| '-' PrimaryExpression
| PrimaryExpression
[88]   PrimaryExpression   ::=   BrackettedExpression | BuiltInCall | iriOrFunction | RDFLiteral | NumericLiteral | BooleanLiteral | Var | ExprTripleTerm
[89]   ExprTripleTerm   ::=   '<<(' ExprTripleTermSubject Verb ExprTripleTermObject ')>>'
[90]   ExprTripleTermSubject   ::=   iri | RDFLiteral | NumericLiteral | BooleanLiteral | Var
[91]   ExprTripleTermObject   ::=   iri | RDFLiteral | NumericLiteral | BooleanLiteral | Var | ExprTripleTerm
[92]   BrackettedExpression   ::=   '(' Expression ')'
[93]   BuiltInCall   ::=     'STR' '(' Expression ')'
| 'LANG' '(' Expression ')'
| 'LANGMATCHES' '(' Expression ',' Expression ')'
| 'LANGDIR' '(' Expression ')'
| 'DATATYPE' '(' Expression ')'
| 'IRI' '(' Expression ')'
| 'URI' '(' Expression ')'
| 'BNODE' ( '(' Expression ')' | NIL )
| 'ABS' '(' Expression ')'
| 'CEIL' '(' Expression ')'
| 'FLOOR' '(' Expression ')'
| 'ROUND' '(' Expression ')'
| 'CONCAT' ExpressionList
| 'SUBSTR' '(' Expression ',' Expression ( ',' Expression )? ')'
| 'STRLEN' '(' Expression ')'
| 'REPLACE' '(' Expression ',' Expression ',' Expression ( ',' Expression )? ')'
| 'UCASE' '(' Expression ')'
| 'LCASE' '(' Expression ')'
| 'ENCODE_FOR_URI' '(' Expression ')'
| 'CONTAINS' '(' Expression ',' Expression ')'
| 'STRSTARTS' '(' Expression ',' Expression ')'
| 'STRENDS' '(' Expression ',' Expression ')'
| 'STRBEFORE' '(' Expression ',' Expression ')'
| 'STRAFTER' '(' Expression ',' Expression ')'
| 'YEAR' '(' Expression ')'
| 'MONTH' '(' Expression ')'
| 'DAY' '(' Expression ')'
| 'HOURS' '(' Expression ')'
| 'MINUTES' '(' Expression ')'
| 'SECONDS' '(' Expression ')'
| 'TIMEZONE' '(' Expression ')'
| 'TZ' '(' Expression ')'
| 'NOW' NIL
| 'UUID' NIL
| 'STRUUID' NIL
| 'IF' '(' Expression ',' Expression ',' Expression ')'
| 'STRLANG' '(' Expression ',' Expression ')'
| 'STRLANGDIR' '(' Expression ',' Expression ',' Expression ')'
| 'STRDT' '(' Expression ',' Expression ')'
| 'sameTerm' '(' Expression ',' Expression ')'
| 'isIRI' '(' Expression ')'
| 'isURI' '(' Expression ')'
| 'isBLANK' '(' Expression ')'
| 'isLITERAL' '(' Expression ')'
| 'isNUMERIC' '(' Expression ')'
| 'hasLANG' '(' Expression ')'
| 'hasLANGDIR' '(' Expression ')'
| 'REGEX' '(' Expression ',' Expression ( ',' Expression )? ')'
| 'isTRIPLE' '(' Expression ')'
| 'TRIPLE' '(' Expression ',' Expression ',' Expression ')'
| 'SUBJECT' '(' Expression ')'
| 'PREDICATE' '(' Expression ')'
| 'OBJECT' '(' Expression ')'
[94]   iriOrFunction   ::=   iri ArgList?
[95]   RDFLiteral   ::=   String ( LANG_DIR | '^^' iri )?
[96]   NumericLiteral   ::=   NumericLiteralUnsigned | NumericLiteralPositive | NumericLiteralNegative
[97]   NumericLiteralUnsigned   ::=   INTEGER | DECIMAL | DOUBLE
[98]   NumericLiteralPositive   ::=   INTEGER_POSITIVE | DECIMAL_POSITIVE | DOUBLE_POSITIVE
[99]   NumericLiteralNegative   ::=   INTEGER_NEGATIVE | DECIMAL_NEGATIVE | DOUBLE_NEGATIVE
[100]   BooleanLiteral   ::=   'true' | 'false'
[101]   String   ::=   STRING_LITERAL1 | STRING_LITERAL2 | STRING_LITERAL_LONG1 | STRING_LITERAL_LONG2
[102]   iri   ::=   IRIREF | PrefixedName
[103]   PrefixedName   ::=   PNAME_LN | PNAME_NS
[104]   BlankNode   ::=   BLANK_NODE_LABEL | ANON

终结符产生式:

[105]   IRIREF   ::=   '<' ([^<>"{}|^`\]-[#x00-#x20] | UCHAR )* '>'
[106]   PNAME_NS   ::=   PN_PREFIX? ':'
[107]   PNAME_LN   ::=   PNAME_NS PN_LOCAL
[108]   BLANK_NODE_LABEL   ::=   '_:' ( PN_CHARS_U | [0-9] ) ((PN_CHARS|'.')* PN_CHARS)?
[109]   VAR1   ::=   '?' VARNAME
[110]   VAR2   ::=   '$' VARNAME
[111]   LANG_DIR   ::=   '@' [a-zA-Z]+ ('-' [a-zA-Z0-9]+)* ('--' [a-zA-Z]+)?
[112]   INTEGER   ::=   [0-9]+
[113]   DECIMAL   ::=   [0-9]* '.' [0-9]+
[114]   DOUBLE   ::=   ( ([0-9]+ ('.'[0-9]*)? ) | ( '.' ([0-9])+ ) ) [eE][+-]?[0-9]+
[115]   INTEGER_POSITIVE   ::=   '+' INTEGER
[116]   DECIMAL_POSITIVE   ::=   '+' DECIMAL
[117]   DOUBLE_POSITIVE   ::=   '+' DOUBLE
[118]   INTEGER_NEGATIVE   ::=   '-' INTEGER
[119]   DECIMAL_NEGATIVE   ::=   '-' DECIMAL
[120]   DOUBLE_NEGATIVE   ::=   '-' DOUBLE
[121]   STRING_LITERAL1   ::=   "'" ( ([^#x27#x5C#xA#xD]) | ECHAR | UCHAR )* "'"
[122]   STRING_LITERAL2   ::=   '"' ( ([^#x22#x5C#xA#xD]) | ECHAR | UCHAR )* '"'
[123]   STRING_LITERAL_LONG1   ::=   "'''" ( ( "'" | "''" )? ( [^'\] | ECHAR | UCHAR ) )* "'''"
[124]   STRING_LITERAL_LONG2   ::=   '"""' ( ( '"' | '""' )? ( [^"\] | ECHAR | UCHAR ) )* '"""'
[125]   ECHAR   ::=   '\' [tbnrf\"']
[126]   UCHAR   ::=   ('\u' HEX HEX HEX HEX) | ('\U' HEX HEX HEX HEX HEX HEX HEX HEX)
[127]   NIL   ::=   '(' WS* ')'
[128]   WS   ::=   #x20 | #x9 | #xD | #xA
[129]   ANON   ::=   '[' WS* ']'
[130]   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]
[131]   PN_CHARS_U   ::=   PN_CHARS_BASE | '_'
[132]   VARNAME   ::=   ( PN_CHARS_U | [0-9] ) ( PN_CHARS_U | [0-9] | #x00B7 | [#x0300-#x036F] | [#x203F-#x2040] )*
[133]   PN_CHARS   ::=   PN_CHARS_U | '-' | [0-9] | #x00B7 | [#x0300-#x036F] | [#x203F-#x2040]
[134]   PN_PREFIX   ::=   PN_CHARS_BASE ((PN_CHARS|'.')* PN_CHARS)?
[135]   PN_LOCAL   ::=   (PN_CHARS_U | ':' | [0-9] | PLX ) ((PN_CHARS | '.' | ':' | PLX)* (PN_CHARS | ':' | PLX) )?
[136]   PLX   ::=   PERCENT | PN_LOCAL_ESC
[137]   PERCENT   ::=   '%' HEX HEX
[138]   HEX   ::=   [0-9] | [A-F] | [a-f]
[139]   PN_LOCAL_ESC   ::=   '\' ( '_' | '~' | '.' | '-' | '!' | '$' | '&' | "'" | '(' | ')' | '*' | '+' | ',' | ';' | '=' | '/' | '?' | '#' | '@' | '%' )

该语法的文本版本可在 此处获取。

7.6 选定的终结符字面 字符串

本文档使用一些特定的终结符字面字符串 [EBNF-NOTATION]。为阐明这些 终结符字面字符串所使用的 Unicode 码点, 下表描述了本节中使用的特定字符。

编码 字形 描述
U+000A LF 换行
U+000D CR 回车
U+0023 # 数字符号
U+0025 % 百分号
U+005C \ 反斜杠

A. 互联网媒体类型和文件 扩展名

形状规则语言的互联网媒体类型(以前称为 MIME 类型)是 "application/shape-rules"。

以下信息已提交给互联网工程指导组(IESG),以供审查、批准并向 IANA 注册。

类型名称:
application
子类型名称:
shape-rules
必需参数:
可选参数:
version
此参数是可选的。 如果存在,version 的可接受值在 版本标签中定义。
profile
此参数是可选的,并用于包含附加信息。 在不了解 profile 的情况下处理时,它不会改变资源表示的语义。 profile 参数的值是一个由空格分隔的非空 URI 列表。 有关更多信息和背景,请参阅 [RFC6906]。
编码考量:
形状规则语言的语法以 Unicode [UNICODE] 中的码点表示。编码始终为 UTF-8 [RFC3629]。
Unicode 码点也可以使用 \uXXXX(U+0 至 U+FFFF)或 \UXXXXXXXX 语法(从 U+10000 起)表示,其中 X 是十六进制数字 [0-9A-F]
安全考量:
参见附录 C,安全考量,以及 UTF-8,一种 ISO 10646 的转换格式 [RFC3629] 第 7 节,安全 考量。
互操作性考量:
不存在已知的互操作性问题。
已发布规范:
本规范。
附加信息:
魔数:
SHACL 规则文件可以在文档开头附近包含字符串 'PREFIX' (大小写无关)。
文件扩展名:
".srl"
基准 URI:
SHACL 规则的 'BASE <IRIref>' 术语可以改变 查询语言中相对 IRIref 的当前基准 URI, 这些相对 IRIref 会在文档后续按顺序使用。
联系人及电子邮件地址,以获取更多信息:
数据形状工作组 <public-shacl@w3.org;
预期用途:
COMMON
使用限制:
作者/变更控制者:
SHACL 1.2 Rules 规范是万维网联盟数据形状工作组的工作成果。 W3C 对这些规范拥有变更 控制权。

B. 安全考量

本节为非规范性内容。

TODO

C. 隐私考量

本节为非规范性内容。

TODO

D. 国际化 考量

本节为非规范性内容。

TODO

E. 致谢

本节为非规范性内容。

TODO

F. 索引

F.1 由本 规范定义的术语

F.2 由引用定义的术语

G. 议题摘要

H. 参考文献

H.1 规范性参考文献

[EBNF-NOTATION]
EBNF 表示法. Tim Bray; Jean Paoli; Michael Sperberg-McQueen; Eve Maler; François Yergeau et al. W3C. W3C 推荐标准. URL: https://www.w3.org/TR/xml/#sec-notation
[I18N-GLOSSARY]
国际化术语表. Richard Ishida; Addison Phillips. W3C. 2024 年 10 月 17 日. W3C 工作组说明. URL: https://www.w3.org/TR/i18n-glossary/
[RDF12-CONCEPTS]
RDF 1.2 概念与抽象数据 模型. Andy Seaborne; Gregg Kellogg; Olaf Hartig; Pierre-Antoine Champin. W3C. 2026 年 4 月 7 日. W3C 候选推荐标准. URL: https://www.w3.org/TR/rdf12-concepts/
[RDF12-SEMANTICS]
RDF 1.2 语义. Peter Patel-Schneider; Enrico Franconi; Dörthe Arndt. W3C. 2026 年 4 月 7 日. W3C 候选推荐标准. URL: https://www.w3.org/TR/rdf12-semantics/
[RDF12-TURTLE]
RDF 1.2 Turtle. Gregg Kellogg; Andy Seaborne; Dominik Tomaszuk. W3C. 2026 年 6 月 12 日. W3C 工作草案. URL: https://www.w3.org/TR/rdf12-turtle/
[RFC2119]
用于 RFC 中表示 要求级别的关键词. S. Bradner. IETF. 1997 年 3 月. 当前最佳实践. URL: https://www.rfc-editor.org/info/rfc2119/
[RFC3629]
UTF-8,一种 ISO 10646 的转换格式. F. Yergeau. IETF. 2003 年 11 月. 互联网标准. URL: https://www.rfc-editor.org/info/rfc3629/
[RFC3986]
统一资源标识符(URI):通用 语法. T. Berners-Lee; R. Fielding; L. Masinter. IETF. 2005 年 1 月. 互联网 标准. URL: https://www.rfc-editor.org/info/rfc3986/
[RFC3987]
国际化资源标识符 (IRI). M. Duerst; M. Suignard. IETF. 2005 年 1 月. 提议标准. URL: https://www.rfc-editor.org/info/rfc3987/
[RFC6906]
'profile' 链接关系 类型. E. Wilde. IETF. 2013 年 3 月. 信息性. URL: https://www.rfc-editor.org/info/rfc6906/
[RFC8174]
RFC 2119 关键词中大写与小写的歧义. B. Leiba. IETF. 2017 年 5 月. 当前最佳实践. URL: https://www.rfc-editor.org/info/rfc8174/
[shacl12-node-expr]
SHACL 1.2 节点表达式. Holger Knublauch; Simon Steyskal; Robert David; David Habgood. W3C. 2026 年 1 月 8 日. FPWD. URL: https://www.w3.org/TR/shacl12-node-expr/
[SPARQL12-QUERY]
SPARQL 1.2 查询语言. Olaf Hartig; Andy Seaborne; Ruben Taelman; Gregory Williams; Thomas Pellissier Tanon. W3C. 2026 年 6 月 25 日. W3C 工作草案. URL: https://www.w3.org/TR/sparql12-query/
[UNICODE]
Unicode 标准. Unicode Consortium. URL: https://www.unicode.org/versions/latest/