Skip to content

Commit ff188c4

Browse files
#498: Started with examples (#513)
* #498: Started with examples * Progress * Generalized scope to be node -> node map, added shnex:arg to use scope * Progress * Finished first pass * Clarified that list functions return at most one node * Fixed shnex:path -> pathValues renaming in examples * Added links to examples
1 parent 13781c8 commit ff188c4

File tree

3 files changed

+270
-4
lines changed

3 files changed

+270
-4
lines changed

shacl12-core/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2616,7 +2616,7 @@ <h2>Node Expressions</h2>
26162616
During evaluation, the engine can access <a>triples</a> related to <code>expr</code> in the <a>shapes graph</a>.</li>
26172617
<li><code>focusGraph</code> is a <a>graph</a>, called the <dfn>focus graph</dfn>. This is the default query graph for the evaluation of the node expression.</li>
26182618
<li><code>focusNode</code> is a <a>node</a>, called the <dfn>input focus node</dfn>. This variable may have no value.</li>
2619-
<li><code>scope</code> is a map from <a href="https://www.w3.org/TR/sparql12-query/#defn_QueryVariable">variable names</a> to individual <a>nodes</a>.
2619+
<li><code>scope</code> is a map from (key) <a>nodes</a> to individual (value) <a>nodes</a>.
26202620
The empty map is written as <code>{}</code>.
26212621
</li>
26222622
</ul>

shacl12-node-expr/index.html

Lines changed: 262 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2189,6 +2189,265 @@ <h3>InstancesOf Expressions</h3>
21892189

21902190
</section>
21912191

2192+
<section id="custom-node-expressions">
2193+
<h2>Custom Node Expressions</h2>
2194+
<p>
2195+
SHACL includes vocabulary terms that can be used to define new <a>node expression functions</a>
2196+
by wrapping other (parameterized) <a>node expressions</a>.
2197+
This makes it possible to extend the library of available SHACL node expressions without having to
2198+
hard-code changes to an engine.
2199+
</p>
2200+
2201+
<section>
2202+
<h3>Custom Named Parameter Functions</h3>
2203+
<p class="syntax">
2204+
<span data-syntax-rule="CustomNamedParameterFunction-syntax">
2205+
A <dfn>custom named parameter function</dfn> is an <a>IRI</a> in a <a>shapes graph</a>
2206+
that is a <a>SHACL instance</a> of <code>sh:NamedParameterExpressionFunction</code> and
2207+
a <a>SHACL subclass</a> of <code>sh:NamedParameterExpression</code>.
2208+
It has a single <a>value</a> for <code>sh:bodyExpression</code> that is a <a>well-formed</a>
2209+
<a>node expression</a>.
2210+
<br/><br/>
2211+
A <a>custom named parameter function</a> declares one or more <a>parameters</a> as <a>values</a>
2212+
of <code>sh:parameter</code>, where each such <a>parameter</a> has exactly one <a>value</a> for
2213+
<code>sh:path</code> and that value is an <a>IRI</a>.
2214+
At least one of the parameters has <code>sh:keyParameter true</code>, declaring the <a>key parameters</a>
2215+
for the function.
2216+
The <a>key parameters</a> of all <a>node expression functions</a>
2217+
(including the built-in ones from the <code>shnex:</code> namespace) must be disjoint.
2218+
<br/><br/>
2219+
<a>Custom named parameter functions</a> can reference the declared <a>parameters</a> using an
2220+
<a>arg expression</a> such as <code>[ shnex:arg ex:param ]</code>, where the <a>value</a> of <code>shnex:arg</code>
2221+
matches the <a>IRI</a> of the <a>parameter</a>'s <code>sh:path</code>.
2222+
</span>
2223+
</p>
2224+
<p class="syntax">
2225+
<span data-syntax-rule="CustomNamedParameterExpression-syntax">
2226+
A <dfn>custom named parameter expression</dfn> is a <a>node expression</a>
2227+
represented by a <a>blank node</a> that has exactly one <a>value</a> for at least one of
2228+
the <a>key parameters</a>.
2229+
</span>
2230+
</p>
2231+
<div class="def" id="CustomNamedParameterExpression-evaluation">
2232+
<div class="def-header">EVALUATION OF CUSTOM NAMED PARAMETER EXPRESSIONS</div>
2233+
<p>
2234+
Let <code>expr</code> be a <a>custom named parameter expression</a> with the
2235+
<a>custom named parameter function</a> <code>f</code>.
2236+
Let <code>body</code> be the <a>value</a> of <code>sh:bodyExpression</code> at <code>f</code>
2237+
in the <a>shapes graph</a>.
2238+
<br/><br/>
2239+
Let <code>argScope</code> be a map of (parameter) <a>nodes</a> as keys and (argument) <a>nodes</a>
2240+
as values, so that each <a>parameter</a> of <code>f</code> has the <a>value</a> of the parameter's
2241+
<code>sh:path</code> from <code>expr</code>.
2242+
For example, if <code>f</code> declares just one <a>parameter</a> with <code>sh:path</code> <code>ex:param</code>
2243+
and <code>expr</code> is <code>[ ex:param 42 ]</code> then <code>argScope</code> is <code>{ ex:param : 42 }</code>.
2244+
<br/><br/>
2245+
The <a>output nodes</a> of <code>expr</code> are computed using
2246+
<code>evalExpr(expr, focusGraph, focusNode, scope) -> evalExpr(body, focusGraph, focusNode, argScope)</code>
2247+
</p>
2248+
</div>
2249+
<p><em>The remainder of this section is informative.</em></p>
2250+
<p>
2251+
The following example defines a new <a>node expression function</a> <code>ex:AverageExpression</code>
2252+
that takes another node expression as input using the <a>key parameter</a> <code>ex:average</code>
2253+
and then calculates the sum of all input nodes and divides it by the number of nodes,
2254+
returning the average value of these nodes.
2255+
</p>
2256+
<aside class="example" id="custom-named-function-example">
2257+
<div class="shapes-graph">
2258+
<div class="turtle">
2259+
ex:AverageExpression
2260+
a sh:NamedParameterExpressionFunction ;
2261+
rdfs:label "Average expression"@en ;
2262+
rdfs:comment "Computes the average of the nodes provided by ex:average." ;
2263+
rdfs:subClassOf sh:NamedParameterExpression ;
2264+
sh:parameter ex:AverageExpression-average ;
2265+
sh:bodyExpression [
2266+
sparql:divide (
2267+
[ shnex:sum [ shnex:arg ex:average ] ]
2268+
[ shnex:count [ shnex:arg ex:average ] ]
2269+
)
2270+
] ;
2271+
.
2272+
ex:AverageExpression-average
2273+
a sh:Parameter ;
2274+
sh:path ex:average ;
2275+
sh:name "average" ;
2276+
sh:description "The nodes of which the average shall be computed." ;
2277+
sh:keyParameter true ;
2278+
.
2279+
</div>
2280+
</div>
2281+
</aside>
2282+
<p>
2283+
This new node expression function can the be used as follows:
2284+
</p>
2285+
<aside class="example">
2286+
<div class="shapes-graph">
2287+
<div class="turtle">
2288+
ex:CompanyShape-averageIncome
2289+
a sh:PropertyShape ;
2290+
sh:path ex:averageIncome ;
2291+
sh:datatype xsd:decimal ;
2292+
sh:values [
2293+
<b>ex:average [
2294+
shnex:pathValues ( ex:employee ex:income )
2295+
]</b>
2296+
] .
2297+
</div>
2298+
</div>
2299+
</aside>
2300+
</section>
2301+
2302+
<section>
2303+
<h3>Custom List Parameter Functions</h3>
2304+
<p class="syntax">
2305+
<span data-syntax-rule="CustomListParameterFunction-syntax">
2306+
A <dfn>custom list parameter function</dfn> is an <a>IRI</a> in a <a>shapes graph</a>
2307+
that is a <a>SHACL instance</a> of <code>sh:ListParameterExpressionFunction</code> and
2308+
a <a>SHACL subclass</a> of <code>sh:ListParameterExpression</code>.
2309+
The <a>IRI</a> of a <a>custom list parameter function</a> is its <a>list parameter property</a>.
2310+
It has a single <a>value</a> for <code>sh:bodyExpression</code> that is a <a>well-formed</a>
2311+
<a>node expression</a>.
2312+
<br/><br/>
2313+
<a>Custom list parameter functions</a> can reference the arguments using an
2314+
<a>arg expression</a> such as <code>[ shnex:arg 0 ]</code> and <code>[ shnex:arg 1 ]</code>
2315+
where the <code>xsd:integer</code> <code>n</code> corresponds to the <code>n</code>th <a>member</a>
2316+
of the arguments list, starting with <code>0</code> as the first member.
2317+
</span>
2318+
</p>
2319+
<p class="syntax">
2320+
<span data-syntax-rule="CustomListParameterExpression-syntax">
2321+
A <dfn>custom list parameter expression</dfn> is a <a>node expression</a>
2322+
represented by a <a>blank node</a> that is the <a>subject</a> of exactly one <a>triple</a>
2323+
and the <a>predicate</a> of that triple is the <a>list parameter property</a> of a
2324+
<a>custom list parameter function</a> in the <a>shapes graph</a>.
2325+
</span>
2326+
</p>
2327+
<div class="def" id="CustomListParameterExpression-evaluation">
2328+
<div class="def-header">EVALUATION OF CUSTOM LIST PARAMETER EXPRESSIONS</div>
2329+
<p>
2330+
Let <code>expr</code> be a <a>custom list parameter expression</a> with the
2331+
<a>custom list parameter function</a> <code>f</code>.
2332+
Let <code>body</code> be the <a>value</a> of <code>sh:bodyExpression</code> at <code>f</code>
2333+
in the <a>shapes graph</a>.
2334+
<br/><br/>
2335+
Let <code>argScope</code> be a map of (parameter index) <a>nodes</a> as keys and (argument) <a>nodes</a>
2336+
as values, so that each list argument of <code>expr</code> has the index of the argument as an
2337+
<code>xsd:integer</code> as key, starting with <code>0</code> for the first argument.
2338+
For example, if <code>expr</code> has arguments <code>( 38 4 )</code> then the
2339+
<code>argScope</code> is <code>{ 0 : 38, 1 : 4 }</code>.
2340+
<br/><br/>
2341+
The <a>output nodes</a> of <code>expr</code> are computed using
2342+
<code>evalExpr(expr, focusGraph, focusNode, scope) -> evalExpr(body, focusGraph, focusNode, argScope)</code>
2343+
where an <a>evaluation failure</a> is reported when there is more than 1 output node.
2344+
</p>
2345+
</div>
2346+
<p><em>The remainder of this section is informative.</em></p>
2347+
<p>
2348+
The following example defines a new <a>node expression function</a> <code>ex:spacedConcat</code>
2349+
that takes two nodes as input and returns a string concatenating the two nodes with a space in between.
2350+
</p>
2351+
<aside class="example" id="custom-list-function-example">
2352+
<div class="shapes-graph">
2353+
<div class="turtle">
2354+
ex:spacedConcat
2355+
a sh:ListParameterExpressionFunction ;
2356+
rdfs:label "Spaced concat expression"@en ;
2357+
rdfs:subClassOf sh:ListParameterExpression ;
2358+
sh:bodyExpression [
2359+
sparql:concat (
2360+
[ shnex:arg 0 ]
2361+
" "
2362+
[ shnex:arg 1 ]
2363+
)
2364+
] .
2365+
</div>
2366+
</div>
2367+
</aside>
2368+
<p>
2369+
This new node expression function can the be used as follows:
2370+
</p>
2371+
<aside class="example">
2372+
<div class="shapes-graph">
2373+
<div class="turtle">
2374+
ex:Person-fullName
2375+
a sh:PropertyShape ;
2376+
sh:path ex:fullName ;
2377+
sh:datatype xsd:string ;
2378+
sh:values [
2379+
<b>ex:spacedConcat (
2380+
[ shnex:pathValues ex:firstName ]
2381+
[ shnex:pathValues ex:lastName ]
2382+
)</b>
2383+
] .
2384+
</div>
2385+
</div>
2386+
</aside>
2387+
</section>
2388+
2389+
<section id="ArgExpression">
2390+
<h3>Arg Expressions</h3>
2391+
<p>
2392+
Custom node expressions can use <code>shnex:arg</code> to access the arguments.
2393+
</p>
2394+
<p class="syntax">
2395+
<span data-syntax-rule="ArgExpression-syntax">
2396+
A <a>blank node</a> that is the <a>subject</a> of the following properties
2397+
is called an <dfn>arg expression</dfn> with the <a>function name</a> <code>shnex:ArgExpression</code>:
2398+
<table class="term-table">
2399+
<thead>
2400+
<th>Property</th>
2401+
<th>Constraints</th>
2402+
<th>Description</th>
2403+
</thead>
2404+
<tbody>
2405+
<tr>
2406+
<td><b><code>shnex:arg</code></b></td>
2407+
<td>
2408+
<code>
2409+
sh:or (<br/>
2410+
&nbsp;&nbsp;[ sh:nodeKind sh:IRI ]<br />
2411+
&nbsp;&nbsp;[ sh:datatype xsd:integer ]<br />
2412+
)
2413+
</code>
2414+
</td>
2415+
<td>
2416+
The argument key, e.g. <code>ex:myParameter</code> or <code>1</code>.
2417+
</td>
2418+
</tr>
2419+
</tbody>
2420+
</table>
2421+
</span>
2422+
</p>
2423+
<div class="def" id="ArgExpression-evaluation">
2424+
<div class="def-header">EVALUATION OF ARG EXPRESSIONS</div>
2425+
<p>
2426+
Let <code>arg</code> be the <a>value</a> of <code>shnex:arg</code> in the <a>arg expression</a>.
2427+
The <a>output nodes</a> of the <a>var expression</a> are computed as follows, in order:
2428+
</p>
2429+
<ol>
2430+
<li>
2431+
if <code>arg</code> is in the <code>scope</code> and has the value <code>a</code> then
2432+
<code>evalExpr(expr, focusGraph, focusNode, scope) -> evalExpr(a, focusGraph, focusNode, {})</code>
2433+
</li>
2434+
<li>otherwise <code>evalExpr(expr, focusGraph, focusNode, scope) -> []</code></li>
2435+
</ol>
2436+
</div>
2437+
<p><em>The remainder of this section is informative.</em></p>
2438+
<p>
2439+
Both <code>shnex:arg</code> and <code>shnex:var</code> access values from the scope.
2440+
The difference is that <code>shnex:arg</code> interprets the values as node expressions, while
2441+
<code>shnex:var</code> treats the values as individual nodes.
2442+
As a result, a custom node expression can evaluate nested node expressions that are passed in as arguments.
2443+
</p>
2444+
<p>
2445+
Examples of <code>shnex:arg</code> can be found in
2446+
<a href="#custom-named-function-example"></a> and <a href="#custom-list-function-example"></a>.
2447+
</p>
2448+
</section>
2449+
</section>
2450+
21922451
<section id="constraint-components">
21932452
<h2>Constraint Components</h2>
21942453
<p>
@@ -2765,7 +3024,7 @@ <h3>Example: Dynamic Minimum Age of Presidents</h3>
27653024
<b>sh:minInclusive [
27663025
shnex:if [
27673026
sparql:eq (
2768-
[ shnex:path ex:country ]
3027+
[ shnex:pathValues ex:country ]
27693028
ex:USA
27703029
)
27713030
]
@@ -2859,7 +3118,7 @@ <h3>Example: Dynamic Enumerations</h3>
28593118
a sh:PropertyShape ;
28603119
sh:path ex:state ;
28613120
<b>sh:in [
2862-
shnex:path ( ex:country ex:stateCode )
3121+
shnex:pathValues ( ex:country ex:stateCode )
28633122
]</b> .
28643123
</div>
28653124
<div class="jsonld">
@@ -2870,7 +3129,7 @@ <h3>Example: Dynamic Enumerations</h3>
28703129
</div>
28713130
</aside>
28723131
<p>
2873-
During validation, a Dynamic SHACL engine will evaluate the path expression at <code>sh:in</code>
3132+
During validation, a Dynamic SHACL engine will evaluate the path values expression at <code>sh:in</code>
28743133
and use the resulting nodes as members of the allowed values.
28753134
Thus, when the value of <code>ex:country</code> is <code>ex:USA</code>, it will look up the
28763135
state codes that are linked to <code>ex:USA</code>.

shacl12-vocabularies/shacl.ttl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1386,6 +1386,13 @@ sh:ListParameterExpression
13861386
rdfs:subClassOf sh:NodeExpression ;
13871387
rdfs:isDefinedBy sh: .
13881388

1389+
sh:bodyExpression
1390+
a rdf:Property ;
1391+
rdfs:label "body expression"@en ;
1392+
rdfs:comment "A node expression that is the implementation/body of a custom node expression function."@en ;
1393+
rdfs:domain sh:NodeExpressionFunction ;
1394+
rdfs:isDefinedBy sh: .
1395+
13891396

13901397
# General SPARQL execution support --------------------------------------------
13911398

0 commit comments

Comments
 (0)