From ec708095914229ef38c440a1c86dd833d7500442 Mon Sep 17 00:00:00 2001 From: Holger Knublauch Date: Sat, 16 Aug 2025 10:59:21 +0300 Subject: [PATCH 1/8] #498: Started with examples --- shacl12-node-expr/index.html | 107 +++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) diff --git a/shacl12-node-expr/index.html b/shacl12-node-expr/index.html index 0200737a..8ffd66dd 100644 --- a/shacl12-node-expr/index.html +++ b/shacl12-node-expr/index.html @@ -2105,6 +2105,113 @@

InstancesOf Expressions

+
+

Custom Node Expressions

+

+ SHACL includes vocabulary that can be used to define new node expression functions + by wrapping another (parameterized) node expression. +

+

+ TODO: Describe syntax and semantics +

+
+

Example: User-Defined Named Parameter Function

+

+ The following example defines a new node expression function ex:AverageExpression + that takes another node expression as input using the key parameter ex:average + and then calculates the sum of all input nodes and divides it by the number of nodes, + returning the average value of these nodes. +

+ +

+ This new node expression function can the be used as follows: +

+ +
+
+

Example: User-Defined List Parameter Function

+

+ The following example defines a new node expression function ex:spacedConcat + that takes two nodes as input and returns a string concatenating the two nodes with a space in between. +

+ +

+ This new node expression function can the be used as follows: +

+ +
+
+

Constraint Components

From 17296aeeec98106b8aff93d07aa3a406d069856b Mon Sep 17 00:00:00 2001 From: Holger Knublauch Date: Sat, 16 Aug 2025 11:23:00 +0300 Subject: [PATCH 2/8] Progress --- shacl12-node-expr/index.html | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/shacl12-node-expr/index.html b/shacl12-node-expr/index.html index 8ffd66dd..6625f722 100644 --- a/shacl12-node-expr/index.html +++ b/shacl12-node-expr/index.html @@ -2112,7 +2112,12 @@

Custom Node Expressions

by wrapping another (parameterized) node expression.

- TODO: Describe syntax and semantics + TODO: Describe syntax and semantics. + In a nutshell, these declarations use the same syntax as the built-in functions, + but also carry a sh:expressionBody. + In this body, you can access the input node expressions using shnex:arg. + This will recursively evaluate those nested expressions. + As these are lists, we cannot use shnex:var (which only supports individual nodes).

Example: User-Defined Named Parameter Function

@@ -2128,7 +2133,7 @@

Example: User-Defined Named Parameter Function

ex:AverageExpression a sh:NamedParameterExpressionFunction ; rdfs:label "Average expression"@en ; - rdfs:comment "Computes the average of the nodes provided by ex:average." ; + rdfs:comment "Computes the average of the nodes provided by ex:average." ; rdfs:subClassOf sh:NamedParameterExpression ; sh:parameter ex:AverageExpression-average ; sh:expressionBody [ @@ -2140,9 +2145,9 @@

Example: User-Defined Named Parameter Function

. ex:AverageExpression-average a sh:Parameter ; - sh:path ex:average ; - sh:name "average" ; - sh:description "The nodes of which the average shall be computed." ; + sh:path ex:average ; + sh:name "average" ; + sh:description "The nodes of which the average shall be computed." ; . From bda7a178a51140ffdef19a2e66ec07a1df401203 Mon Sep 17 00:00:00 2001 From: Holger Knublauch Date: Tue, 2 Sep 2025 14:47:32 +0300 Subject: [PATCH 3/8] Generalized scope to be node -> node map, added shnex:arg to use scope --- shacl12-core/index.html | 2 +- shacl12-node-expr/index.html | 59 ++++++++++++++++++++++++++++++++++-- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/shacl12-core/index.html b/shacl12-core/index.html index fcf6d9c9..0efbb6db 100644 --- a/shacl12-core/index.html +++ b/shacl12-core/index.html @@ -2616,7 +2616,7 @@

Node Expressions

During evaluation, the engine can access triples related to expr in the shapes graph.
  • focusGraph is a graph, called the focus graph. This is the default query graph for the evaluation of the node expression.
  • focusNode is a node, called the input focus node. This variable may have no value.
  • -
  • scope is a map from variable names to individual nodes. +
  • scope is a map from (key) nodes to individual (value) nodes. The empty map is written as {}.
  • diff --git a/shacl12-node-expr/index.html b/shacl12-node-expr/index.html index 7756f710..7ec6b2d0 100644 --- a/shacl12-node-expr/index.html +++ b/shacl12-node-expr/index.html @@ -2180,6 +2180,7 @@

    Example: User-Defined Named Parameter Function

    sh:path ex:average ; sh:name "average" ; sh:description "The nodes of which the average shall be computed." ; + sh:keyParameter true ; . @@ -2196,9 +2197,9 @@

    Example: User-Defined Named Parameter Function

    sh:datatype xsd:decimal ; sh:values [ ex:average [ - shnex:path ( ex:employee ex:income ) + shnex:pathValues ( ex:employee ex:income ) ] - ] + ] . @@ -2247,6 +2248,60 @@

    Example: User-Defined List Parameter Function

    + +
    +

    Arg Expressions

    +

    + + A blank node that is the subject of the following properties + is called an arg expression with the function name shnex:ArgExpression: + + + + + + + + + + + + + +
    PropertyConstraintsDescription
    shnex:arg + + sh:or ( + [ sh:nodeKind sh:IRI ] + [ sh:datatype xsd:integer ] + ) + + + The argument key, e.g. ex:myParameter or 1. +
    +
    +

    +
    +
    EVALUATION OF ARG EXPRESSIONS
    +

    + Let arg be the value of shnex:arg in the arg expression. + The output nodes of the var expression are computed as follows, in order: +

    +
      +
    1. + if arg is in the scope and has the value a then + evalExpr(expr, focusGraph, focusNode, scope) -> evalExpr(a, focusGraph, focusNode, {}) +
    2. +
    3. otherwise evalExpr(expr, focusGraph, focusNode, scope) -> []
    4. +
    +
    +

    The remainder of this section is informative.

    +

    + Both shnex:arg and shnex:var access values from the scope. + The difference is that shnex:arg interprets the values as node expressions, while + shnex:var treats the values as individual nodes. + As a result, a custom node expression can evaluate nested node expressions that as passed in as arguments. +

    +
    From 22f8259f5cd6f0165f1051fe4411d156f195e7be Mon Sep 17 00:00:00 2001 From: Holger Knublauch Date: Tue, 2 Sep 2025 17:40:25 +0300 Subject: [PATCH 4/8] Progress --- shacl12-node-expr/index.html | 27 ++++++++++++++++----------- shacl12-vocabularies/shacl.ttl | 7 +++++++ 2 files changed, 23 insertions(+), 11 deletions(-) diff --git a/shacl12-node-expr/index.html b/shacl12-node-expr/index.html index 7ec6b2d0..d65da234 100644 --- a/shacl12-node-expr/index.html +++ b/shacl12-node-expr/index.html @@ -2140,13 +2140,15 @@

    InstancesOf Expressions

    Custom Node Expressions

    - SHACL includes vocabulary that can be used to define new node expression functions + SHACL includes vocabulary terms that can be used to define new node expression functions by wrapping another (parameterized) node expression. + This makes it possible to extend the library of available SHACL node expressions without hard-coded + changes to an engine.

    TODO: Describe syntax and semantics. In a nutshell, these declarations use the same syntax as the built-in functions, - but also carry a sh:expressionBody. + but also carry a sh:bodyExpression. In this body, you can access the input node expressions using shnex:arg. This will recursively evaluate those nested expressions. As these are lists, we cannot use shnex:var (which only supports individual nodes). @@ -2168,7 +2170,7 @@

    Example: User-Defined Named Parameter Function

    rdfs:comment "Computes the average of the nodes provided by ex:average." ; rdfs:subClassOf sh:NamedParameterExpression ; sh:parameter ex:AverageExpression-average ; - sh:expressionBody [ + sh:bodyExpression [ sparql:divide ( [ shnex:sum [ shnex:arg ex:average ] ] [ shnex:count [ shnex:arg ex:average ] ] @@ -2217,7 +2219,7 @@

    Example: User-Defined List Parameter Function

    a sh:ListParameterExpressionFunction ; rdfs:label "Spaced concat expression"@en ; rdfs:subClassOf sh:ListParameterExpression ; - sh:expressionBody [ + sh:bodyExpression [ sparql:concat ( [ shnex:arg 0 ] " " @@ -2251,6 +2253,9 @@

    Example: User-Defined List Parameter Function

    Arg Expressions

    +

    + Custom node expressions can use shnex:arg to access the arguments. +

    A blank node that is the subject of the following properties @@ -2266,9 +2271,9 @@

    Arg Expressions

    shnex:arg - sh:or ( - [ sh:nodeKind sh:IRI ] - [ sh:datatype xsd:integer ] + sh:or (
    +   [ sh:nodeKind sh:IRI ]
    +   [ sh:datatype xsd:integer ]
    )
    @@ -2296,7 +2301,7 @@

    Arg Expressions

    The remainder of this section is informative.

    - Both shnex:arg and shnex:var access values from the scope. + Both shnex:arg and shnex:var access values from the scope. The difference is that shnex:arg interprets the values as node expressions, while shnex:var treats the values as individual nodes. As a result, a custom node expression can evaluate nested node expressions that as passed in as arguments. @@ -2961,7 +2966,7 @@

    Example: Dynamic Enumerations

    - Using this extra information, we can now define a sh:in constraint using a Path Expression: + Using this extra information, we can now define a sh:in constraint using a Path Values Expression:

    - During validation, a Dynamic SHACL engine will evaluate the path expression at sh:in + During validation, a Dynamic SHACL engine will evaluate the path values expression at sh:in and use the resulting nodes as members of the allowed values. Thus, when the value of ex:country is ex:USA, it will look up the state codes that are linked to ex:USA. diff --git a/shacl12-vocabularies/shacl.ttl b/shacl12-vocabularies/shacl.ttl index 71d6333a..ed980e9b 100644 --- a/shacl12-vocabularies/shacl.ttl +++ b/shacl12-vocabularies/shacl.ttl @@ -1386,6 +1386,13 @@ sh:ListParameterExpression rdfs:subClassOf sh:NodeExpression ; rdfs:isDefinedBy sh: . +sh:bodyExpression + a rdf:Property ; + rdfs:label "body expression"@en ; + rdfs:comment "A node expression that is the implementation/body of a custom node expression function."@en ; + rdfs:domain sh:NodeExpressionFunction ; + rdfs:isDefinedBy sh: . + # General SPARQL execution support -------------------------------------------- From 33472b9c59e2151a40e10cd8228ffac19da06295 Mon Sep 17 00:00:00 2001 From: Holger Knublauch Date: Wed, 3 Sep 2025 10:37:34 +0300 Subject: [PATCH 5/8] Finished first pass --- shacl12-node-expr/index.html | 117 ++++++++++++++++++++++++++++++----- 1 file changed, 100 insertions(+), 17 deletions(-) diff --git a/shacl12-node-expr/index.html b/shacl12-node-expr/index.html index d65da234..9aea0cff 100644 --- a/shacl12-node-expr/index.html +++ b/shacl12-node-expr/index.html @@ -2141,20 +2141,60 @@

    InstancesOf Expressions

    Custom Node Expressions

    SHACL includes vocabulary terms that can be used to define new node expression functions - by wrapping another (parameterized) node expression. - This makes it possible to extend the library of available SHACL node expressions without hard-coded - changes to an engine. + by wrapping other (parameterized) node expressions. + This makes it possible to extend the library of available SHACL node expressions without having to + hard-code changes to an engine.

    -

    - TODO: Describe syntax and semantics. - In a nutshell, these declarations use the same syntax as the built-in functions, - but also carry a sh:bodyExpression. - In this body, you can access the input node expressions using shnex:arg. - This will recursively evaluate those nested expressions. - As these are lists, we cannot use shnex:var (which only supports individual nodes). -

    -
    -

    Example: User-Defined Named Parameter Function

    + +
    +

    Custom Named Parameter Functions

    +

    + + A custom named parameter function is an IRI in a shapes graph + that is a SHACL instance of sh:NamedParameterExpressionFunction and + a SHACL subclass of sh:NamedParameterExpression. + It has a single value for sh:bodyExpression that is a well-formed + node expression. +

    + A custom named parameter function declares one or more parameters as values + of sh:parameter, where each such parameter has exactly one value for + sh:path and that value is an IRI. + At least one of the parameters has sh:keyParameter true, declaring the key parameters + for the function. + The key parameters of all node expression functions + (including the built-in ones from the shnex: namespace) must be disjoint. +

    + Custom named parameter functions can reference the declared parameters using an + arg expression such as [ shnex:arg ex:param ], where the value of shnex:arg + matches the IRI of the parameter's sh:path. +
    +

    +

    + + A custom named parameter expression is a node expression + represented by a blank node that has exactly one value for at least one of + the key parameters. + +

    +
    +
    EVALUATION OF CUSTOM NAMED PARAMETER EXPRESSIONS
    +

    + Let expr be a custom named parameter expression with the + custom named parameter function f. + Let body be the value of sh:bodyExpression at f + in the shapes graph. +

    + Let argScope be a map of (parameter) nodes as keys and (argument) nodes + as values, so that each parameter of f has the value of the parameter's + sh:path from expr. + For example, if f declares just one parameter with sh:path ex:param + and expr is [ ex:param 42 ] then argScope is { ex:param : 42 }. +

    + The output nodes of expr are computed using + evalExpr(expr, focusGraph, focusNode, scope) -> evalExpr(body, focusGraph, focusNode, argScope) +

    +
    +

    The remainder of this section is informative.

    The following example defines a new node expression function ex:AverageExpression that takes another node expression as input using the key parameter ex:average @@ -2175,7 +2215,7 @@

    Example: User-Defined Named Parameter Function

    [ shnex:sum [ shnex:arg ex:average ] ] [ shnex:count [ shnex:arg ex:average ] ] ) - ] + ] ; . ex:AverageExpression-average a sh:Parameter ; @@ -2206,8 +2246,51 @@

    Example: User-Defined Named Parameter Function

    -
    -

    Example: User-Defined List Parameter Function

    + +
    +

    Custom List Parameter Functions

    +

    + + A custom list parameter function is an IRI in a shapes graph + that is a SHACL instance of sh:ListParameterExpressionFunction and + a SHACL subclass of sh:ListParameterExpression. + The IRI of a custom list parameter function is its list parameter property. + It has a single value for sh:bodyExpression that is a well-formed + node expression. +

    + Custom list parameter functions can reference the arguments using an + arg expression such as [ shnex:arg 0 ] and [ shnex:arg 1 ] + where the xsd:integer n corresponds to the nth member + of the arguments list, starting with 0 as the first member. +
    +

    +

    + + A custom list parameter expression is a node expression + represented by a blank node that is the subject of exactly one triple + and the predicate of that triple is the list parameter property of a + custom list parameter function in the shapes graph. + +

    +
    +
    EVALUATION OF CUSTOM LIST PARAMETER EXPRESSIONS
    +

    + Let expr be a custom list parameter expression with the + custom list parameter function f. + Let body be the value of sh:bodyExpression at f + in the shapes graph. +

    + Let argScope be a map of (parameter index) nodes as keys and (argument) nodes + as values, so that each list argument of expr has the index of the argument as an + xsd:integer as key, starting with 0 for the first argument. + For example, if expr has arguments ( 38 4 ) then the + argScope is { 0 : 38, 1 : 4 }. +

    + The output nodes of expr are computed using + evalExpr(expr, focusGraph, focusNode, scope) -> evalExpr(body, focusGraph, focusNode, argScope) +

    +
    +

    The remainder of this section is informative.

    The following example defines a new node expression function ex:spacedConcat that takes two nodes as input and returns a string concatenating the two nodes with a space in between. @@ -2304,7 +2387,7 @@

    Arg Expressions

    Both shnex:arg and shnex:var access values from the scope. The difference is that shnex:arg interprets the values as node expressions, while shnex:var treats the values as individual nodes. - As a result, a custom node expression can evaluate nested node expressions that as passed in as arguments. + As a result, a custom node expression can evaluate nested node expressions that are passed in as arguments.

    From e4de51a6b5469fbfde27f45d1d8097022d96d008 Mon Sep 17 00:00:00 2001 From: Holger Knublauch Date: Thu, 4 Sep 2025 11:02:20 +0300 Subject: [PATCH 6/8] Clarified that list functions return at most one node --- shacl12-node-expr/index.html | 1 + 1 file changed, 1 insertion(+) diff --git a/shacl12-node-expr/index.html b/shacl12-node-expr/index.html index 9aea0cff..dbb9718e 100644 --- a/shacl12-node-expr/index.html +++ b/shacl12-node-expr/index.html @@ -2288,6 +2288,7 @@

    Custom List Parameter Functions



    The output nodes of expr are computed using evalExpr(expr, focusGraph, focusNode, scope) -> evalExpr(body, focusGraph, focusNode, argScope) + where an evaluation failure is reported when there is more than 1 output node.

    The remainder of this section is informative.

    From eb2b21a32b1e74f6d761eba88d5cc64efea47c78 Mon Sep 17 00:00:00 2001 From: Holger Knublauch Date: Thu, 4 Sep 2025 12:00:43 +0300 Subject: [PATCH 7/8] Fixed shnex:path -> pathValues renaming in examples --- shacl12-node-expr/index.html | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/shacl12-node-expr/index.html b/shacl12-node-expr/index.html index dbb9718e..330104e8 100644 --- a/shacl12-node-expr/index.html +++ b/shacl12-node-expr/index.html @@ -2309,8 +2309,7 @@

    Custom List Parameter Functions

    " " [ shnex:arg 1 ] ) - ] -. + ] . @@ -2326,10 +2325,10 @@

    Custom List Parameter Functions

    sh:datatype xsd:string ; sh:values [ ex:spacedConcat ( - [ shnex:path ex:firstName ] - [ shnex:path ex:lastName ] + [ shnex:pathValues ex:firstName ] + [ shnex:pathValues ex:lastName ] ) - ] + ] . @@ -2969,7 +2968,7 @@

    Example: Dynamic Minimum Age of Presidents

    sh:minInclusive [ shnex:if [ sparql:eq ( - [ shnex:path ex:country ] + [ shnex:pathValues ex:country ] ex:USA ) ] From e15a648c6fefce80c2afbff0be22959bd4f57ccb Mon Sep 17 00:00:00 2001 From: Holger Knublauch Date: Mon, 8 Sep 2025 08:57:34 +0200 Subject: [PATCH 8/8] Added links to examples --- shacl12-node-expr/index.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/shacl12-node-expr/index.html b/shacl12-node-expr/index.html index ceeb3e65..d856271b 100644 --- a/shacl12-node-expr/index.html +++ b/shacl12-node-expr/index.html @@ -2442,8 +2442,8 @@

    Arg Expressions

    As a result, a custom node expression can evaluate nested node expressions that are passed in as arguments.

    - Examples of shnex:arg can be found in the examples - and . + Examples of shnex:arg can be found in + and .