Skip to content

Commit

Permalink
Make SHACL regex patterns follow Java
Browse files Browse the repository at this point in the history
We included the regex pattern as-is from the input. Instead, with this
patch, we parse it from the input and re-render it into the form that we
also use in Java. We pick Java regex dialect as most SHACL validators in
the wild rely on Java as platform, so we decide to serve this user base
with priority.

For example, in the input meta-model specification, we omit the minimum
bound 0 (*e.g.*, ``{,4}``), which breaks with the Java regex engine
beneath the SHACL validator. Now, the pattern is correctly rendered with an
explicit 0 (``{0,4}``).

Discovered in [aas-core-meta issue 342].

[aas-core-meta issue 342]: aas-core-works/aas-core-meta#342
  • Loading branch information
mristin committed Aug 26, 2024
1 parent 0f7345e commit f3bb96d
Show file tree
Hide file tree
Showing 2 changed files with 60 additions and 33 deletions.
29 changes: 28 additions & 1 deletion aas_core_codegen/rdf_shacl/shacl.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from aas_core_codegen import intermediate, specific_implementations, infer_for_schema
from aas_core_codegen.common import Stripped, Error, assert_never, Identifier
from aas_core_codegen.parse import retree as parse_retree
from aas_core_codegen.rdf_shacl import (
naming as rdf_shacl_naming,
common as rdf_shacl_common,
Expand Down Expand Up @@ -214,7 +215,33 @@ def _define_property_shape(
# region Define patterns

for pattern_constraint in pattern_constraints:
pattern_literal = rdf_shacl_common.string_literal(pattern_constraint.pattern)
# NOTE (mristin):
# We need to render the regular expression so that the pattern appears in
# the canonical form. The original pattern in the specification might be written
# in Python dialect, which does not translate directly to many Regex Engines.
#
# For example, repetition bounds can be given with 0 omitted (*e.g.*, ``{,4}``),
# while SHACL and Java need an explicit zero (``{0, 4}``). Our standard renderer
# puts an explicit zero.

regex, parse_error = parse_retree.parse([pattern_constraint.pattern])
if parse_error is not None:
return None, Error(
prop.parsed.node,
f"(mristin): "
f"The pattern could not be parsed: {pattern_constraint.pattern}",
)
assert regex is not None

rendered_pattern = parse_retree.render(regex)

assert len(rendered_pattern) == 1 and isinstance(rendered_pattern[0], str), (
"Expected strictly only a string output from rendering "
f"a string pattern (*e.g.*, no FormattedValues), "
f"but got: {rendered_pattern}"
)

pattern_literal = rdf_shacl_common.string_literal(rendered_pattern[0])

stmts.append(Stripped(f"sh:pattern {pattern_literal} ;"))

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ aas:AbstractLangStringShape a sh:NodeShape ;
sh:datatype xs:string ;
sh:minCount 1 ;
sh:maxCount 1 ;
sh:pattern "^(([a-zA-Z]{2,3}(-[a-zA-Z]{3}(-[a-zA-Z]{3}){,2})?|[a-zA-Z]{4}|[a-zA-Z]{5,8})(-[a-zA-Z]{4})?(-([a-zA-Z]{2}|[0-9]{3}))?(-(([a-zA-Z0-9]){5,8}|[0-9]([a-zA-Z0-9]){3}))*(-[0-9A-WY-Za-wy-z](-([a-zA-Z0-9]){2,8})+)*(-[xX](-([a-zA-Z0-9]){1,8})+)?|[xX](-([a-zA-Z0-9]){1,8})+|((en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)|(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang)))$" ;
sh:pattern "^(([a-zA-Z]{2,3}(-[a-zA-Z]{3}(-[a-zA-Z]{3}){0,2})?|[a-zA-Z]{4}|[a-zA-Z]{5,8})(-[a-zA-Z]{4})?(-([a-zA-Z]{2}|[0-9]{3}))?(-(([a-zA-Z0-9]){5,8}|[0-9]([a-zA-Z0-9]){3}))*(-[0-9A-WY-Za-wy-z](-([a-zA-Z0-9]){2,8})+)*(-[xX](-([a-zA-Z0-9]){1,8})+)?|[xX](-([a-zA-Z0-9]){1,8})+|((en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)|(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang)))$" ;
] ;
sh:property [
a sh:PropertyShape ;
Expand All @@ -45,7 +45,7 @@ aas:AbstractLangStringShape a sh:NodeShape ;
sh:minCount 1 ;
sh:maxCount 1 ;
sh:minLength 1 ;
sh:pattern "^[\\x09\\x0A\\x0D\\x20-\\uD7FF\\uE000-\\uFFFD\\U00010000-\\U0010FFFF]*$" ;
sh:pattern "^[\\x09\\x0a\\x0d\\x20-\\ud7ff\\ue000-\\ufffd\\U00010000-\\U0010ffff]*$" ;
] ;
.

Expand All @@ -60,7 +60,7 @@ aas:AdministrativeInformationShape a sh:NodeShape ;
sh:maxCount 1 ;
sh:minLength 1 ;
sh:maxLength 4 ;
sh:pattern "^[\\x09\\x0A\\x0D\\x20-\\uD7FF\\uE000-\\uFFFD\\U00010000-\\U0010FFFF]*$" ;
sh:pattern "^[\\x09\\x0a\\x0d\\x20-\\ud7ff\\ue000-\\ufffd\\U00010000-\\U0010ffff]*$" ;
sh:pattern "^(0|[1-9][0-9]*)$" ;
] ;
sh:property [
Expand All @@ -71,7 +71,7 @@ aas:AdministrativeInformationShape a sh:NodeShape ;
sh:maxCount 1 ;
sh:minLength 1 ;
sh:maxLength 4 ;
sh:pattern "^[\\x09\\x0A\\x0D\\x20-\\uD7FF\\uE000-\\uFFFD\\U00010000-\\U0010FFFF]*$" ;
sh:pattern "^[\\x09\\x0a\\x0d\\x20-\\ud7ff\\ue000-\\ufffd\\U00010000-\\U0010ffff]*$" ;
sh:pattern "^(0|[1-9][0-9]*)$" ;
] ;
sh:property [
Expand All @@ -89,7 +89,7 @@ aas:AdministrativeInformationShape a sh:NodeShape ;
sh:maxCount 1 ;
sh:minLength 1 ;
sh:maxLength 2000 ;
sh:pattern "^[\\x09\\x0A\\x0D\\x20-\\uD7FF\\uE000-\\uFFFD\\U00010000-\\U0010FFFF]*$" ;
sh:pattern "^[\\x09\\x0a\\x0d\\x20-\\ud7ff\\ue000-\\ufffd\\U00010000-\\U0010ffff]*$" ;
] ;
.

Expand Down Expand Up @@ -147,7 +147,7 @@ aas:AssetInformationShape a sh:NodeShape ;
sh:maxCount 1 ;
sh:minLength 1 ;
sh:maxLength 2000 ;
sh:pattern "^[\\x09\\x0A\\x0D\\x20-\\uD7FF\\uE000-\\uFFFD\\U00010000-\\U0010FFFF]*$" ;
sh:pattern "^[\\x09\\x0a\\x0d\\x20-\\ud7ff\\ue000-\\ufffd\\U00010000-\\U0010ffff]*$" ;
] ;
sh:property [
a sh:PropertyShape ;
Expand All @@ -163,7 +163,7 @@ aas:AssetInformationShape a sh:NodeShape ;
sh:maxCount 1 ;
sh:minLength 1 ;
sh:maxLength 2000 ;
sh:pattern "^[\\x09\\x0A\\x0D\\x20-\\uD7FF\\uE000-\\uFFFD\\U00010000-\\U0010FFFF]*$" ;
sh:pattern "^[\\x09\\x0a\\x0d\\x20-\\ud7ff\\ue000-\\ufffd\\U00010000-\\U0010ffff]*$" ;
] ;
sh:property [
a sh:PropertyShape ;
Expand Down Expand Up @@ -206,7 +206,7 @@ aas:BasicEventElementShape a sh:NodeShape ;
sh:maxCount 1 ;
sh:minLength 1 ;
sh:maxLength 255 ;
sh:pattern "^[\\x09\\x0A\\x0D\\x20-\\uD7FF\\uE000-\\uFFFD\\U00010000-\\U0010FFFF]*$" ;
sh:pattern "^[\\x09\\x0a\\x0d\\x20-\\ud7ff\\ue000-\\ufffd\\U00010000-\\U0010ffff]*$" ;
] ;
sh:property [
a sh:PropertyShape ;
Expand Down Expand Up @@ -259,8 +259,8 @@ aas:BlobShape a sh:NodeShape ;
sh:maxCount 1 ;
sh:minLength 1 ;
sh:maxLength 100 ;
sh:pattern "^[\\x09\\x0A\\x0D\\x20-\\uD7FF\\uE000-\\uFFFD\\U00010000-\\U0010FFFF]*$" ;
sh:pattern "^([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+/([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+([ ]*;[ ]*([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+=(([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+|\"(([ !#-\\[\\]-~]|[\\x80-\\xff])|\\\\([ !-~]|[\\x80-\\xff]))*\"))*$" ;
sh:pattern "^[\\x09\\x0a\\x0d\\x20-\\ud7ff\\ue000-\\ufffd\\U00010000-\\U0010ffff]*$" ;
sh:pattern "^([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+/([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+([ \\t]*;[ \\t]*([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+=(([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+|\"(([\\t !#-\\[\\]-~]|[\\x80-\\xff])|\\\\([\\t !-~]|[\\x80-\\xff]))*\"))*$" ;
] ;
.

Expand Down Expand Up @@ -336,7 +336,7 @@ aas:DataSpecificationIec61360Shape a sh:NodeShape ;
sh:minCount 0 ;
sh:maxCount 1 ;
sh:minLength 1 ;
sh:pattern "^[\\x09\\x0A\\x0D\\x20-\\uD7FF\\uE000-\\uFFFD\\U00010000-\\U0010FFFF]*$" ;
sh:pattern "^[\\x09\\x0a\\x0d\\x20-\\ud7ff\\ue000-\\ufffd\\U00010000-\\U0010ffff]*$" ;
] ;
sh:property [
a sh:PropertyShape ;
Expand All @@ -352,7 +352,7 @@ aas:DataSpecificationIec61360Shape a sh:NodeShape ;
sh:minCount 0 ;
sh:maxCount 1 ;
sh:minLength 1 ;
sh:pattern "^[\\x09\\x0A\\x0D\\x20-\\uD7FF\\uE000-\\uFFFD\\U00010000-\\U0010FFFF]*$" ;
sh:pattern "^[\\x09\\x0a\\x0d\\x20-\\ud7ff\\ue000-\\ufffd\\U00010000-\\U0010ffff]*$" ;
] ;
sh:property [
a sh:PropertyShape ;
Expand All @@ -361,7 +361,7 @@ aas:DataSpecificationIec61360Shape a sh:NodeShape ;
sh:minCount 0 ;
sh:maxCount 1 ;
sh:minLength 1 ;
sh:pattern "^[\\x09\\x0A\\x0D\\x20-\\uD7FF\\uE000-\\uFFFD\\U00010000-\\U0010FFFF]*$" ;
sh:pattern "^[\\x09\\x0a\\x0d\\x20-\\ud7ff\\ue000-\\ufffd\\U00010000-\\U0010ffff]*$" ;
] ;
sh:property [
a sh:PropertyShape ;
Expand All @@ -383,7 +383,7 @@ aas:DataSpecificationIec61360Shape a sh:NodeShape ;
sh:minCount 0 ;
sh:maxCount 1 ;
sh:minLength 1 ;
sh:pattern "^[\\x09\\x0A\\x0D\\x20-\\uD7FF\\uE000-\\uFFFD\\U00010000-\\U0010FFFF]*$" ;
sh:pattern "^[\\x09\\x0a\\x0d\\x20-\\ud7ff\\ue000-\\ufffd\\U00010000-\\U0010ffff]*$" ;
] ;
sh:property [
a sh:PropertyShape ;
Expand All @@ -400,7 +400,7 @@ aas:DataSpecificationIec61360Shape a sh:NodeShape ;
sh:maxCount 1 ;
sh:minLength 1 ;
sh:maxLength 2000 ;
sh:pattern "^[\\x09\\x0A\\x0D\\x20-\\uD7FF\\uE000-\\uFFFD\\U00010000-\\U0010FFFF]*$" ;
sh:pattern "^[\\x09\\x0a\\x0d\\x20-\\ud7ff\\ue000-\\ufffd\\U00010000-\\U0010ffff]*$" ;
] ;
sh:property [
a sh:PropertyShape ;
Expand Down Expand Up @@ -453,7 +453,7 @@ aas:EntityShape a sh:NodeShape ;
sh:maxCount 1 ;
sh:minLength 1 ;
sh:maxLength 2000 ;
sh:pattern "^[\\x09\\x0A\\x0D\\x20-\\uD7FF\\uE000-\\uFFFD\\U00010000-\\U0010FFFF]*$" ;
sh:pattern "^[\\x09\\x0a\\x0d\\x20-\\ud7ff\\ue000-\\ufffd\\U00010000-\\U0010ffff]*$" ;
] ;
sh:property [
a sh:PropertyShape ;
Expand Down Expand Up @@ -540,7 +540,7 @@ aas:EventPayloadShape a sh:NodeShape ;
sh:maxCount 1 ;
sh:minLength 1 ;
sh:maxLength 255 ;
sh:pattern "^[\\x09\\x0A\\x0D\\x20-\\uD7FF\\uE000-\\uFFFD\\U00010000-\\U0010FFFF]*$" ;
sh:pattern "^[\\x09\\x0a\\x0d\\x20-\\ud7ff\\ue000-\\ufffd\\U00010000-\\U0010ffff]*$" ;
] ;
sh:property [
a sh:PropertyShape ;
Expand Down Expand Up @@ -577,7 +577,7 @@ aas:ExtensionShape a sh:NodeShape ;
sh:maxCount 1 ;
sh:minLength 1 ;
sh:maxLength 128 ;
sh:pattern "^[\\x09\\x0A\\x0D\\x20-\\uD7FF\\uE000-\\uFFFD\\U00010000-\\U0010FFFF]*$" ;
sh:pattern "^[\\x09\\x0a\\x0d\\x20-\\ud7ff\\ue000-\\ufffd\\U00010000-\\U0010ffff]*$" ;
] ;
sh:property [
a sh:PropertyShape ;
Expand Down Expand Up @@ -612,7 +612,7 @@ aas:FileShape a sh:NodeShape ;
sh:maxCount 1 ;
sh:minLength 1 ;
sh:maxLength 2000 ;
sh:pattern "^[\\x09\\x0A\\x0D\\x20-\\uD7FF\\uE000-\\uFFFD\\U00010000-\\U0010FFFF]*$" ;
sh:pattern "^[\\x09\\x0a\\x0d\\x20-\\ud7ff\\ue000-\\ufffd\\U00010000-\\U0010ffff]*$" ;
] ;
sh:property [
a sh:PropertyShape ;
Expand All @@ -622,8 +622,8 @@ aas:FileShape a sh:NodeShape ;
sh:maxCount 1 ;
sh:minLength 1 ;
sh:maxLength 100 ;
sh:pattern "^[\\x09\\x0A\\x0D\\x20-\\uD7FF\\uE000-\\uFFFD\\U00010000-\\U0010FFFF]*$" ;
sh:pattern "^([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+/([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+([ ]*;[ ]*([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+=(([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+|\"(([ !#-\\[\\]-~]|[\\x80-\\xff])|\\\\([ !-~]|[\\x80-\\xff]))*\"))*$" ;
sh:pattern "^[\\x09\\x0a\\x0d\\x20-\\ud7ff\\ue000-\\ufffd\\U00010000-\\U0010ffff]*$" ;
sh:pattern "^([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+/([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+([ \\t]*;[ \\t]*([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+=(([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+|\"(([\\t !#-\\[\\]-~]|[\\x80-\\xff])|\\\\([\\t !-~]|[\\x80-\\xff]))*\"))*$" ;
] ;
.

Expand Down Expand Up @@ -753,7 +753,7 @@ aas:IdentifiableShape a sh:NodeShape ;
sh:maxCount 1 ;
sh:minLength 1 ;
sh:maxLength 2000 ;
sh:pattern "^[\\x09\\x0A\\x0D\\x20-\\uD7FF\\uE000-\\uFFFD\\U00010000-\\U0010FFFF]*$" ;
sh:pattern "^[\\x09\\x0a\\x0d\\x20-\\ud7ff\\ue000-\\ufffd\\U00010000-\\U0010ffff]*$" ;
] ;
.

Expand All @@ -774,7 +774,7 @@ aas:KeyShape a sh:NodeShape ;
sh:maxCount 1 ;
sh:minLength 1 ;
sh:maxLength 2000 ;
sh:pattern "^[\\x09\\x0A\\x0D\\x20-\\uD7FF\\uE000-\\uFFFD\\U00010000-\\U0010FFFF]*$" ;
sh:pattern "^[\\x09\\x0a\\x0d\\x20-\\ud7ff\\ue000-\\ufffd\\U00010000-\\U0010ffff]*$" ;
] ;
.

Expand Down Expand Up @@ -993,7 +993,7 @@ aas:QualifierShape a sh:NodeShape ;
sh:maxCount 1 ;
sh:minLength 1 ;
sh:maxLength 128 ;
sh:pattern "^[\\x09\\x0A\\x0D\\x20-\\uD7FF\\uE000-\\uFFFD\\U00010000-\\U0010FFFF]*$" ;
sh:pattern "^[\\x09\\x0a\\x0d\\x20-\\ud7ff\\ue000-\\ufffd\\U00010000-\\U0010ffff]*$" ;
] ;
sh:property [
a sh:PropertyShape ;
Expand Down Expand Up @@ -1067,7 +1067,7 @@ aas:ReferableShape a sh:NodeShape ;
sh:maxCount 1 ;
sh:minLength 1 ;
sh:maxLength 128 ;
sh:pattern "^[\\x09\\x0A\\x0D\\x20-\\uD7FF\\uE000-\\uFFFD\\U00010000-\\U0010FFFF]*$" ;
sh:pattern "^[\\x09\\x0a\\x0d\\x20-\\ud7ff\\ue000-\\ufffd\\U00010000-\\U0010ffff]*$" ;
] ;
sh:property [
a sh:PropertyShape ;
Expand All @@ -1077,7 +1077,7 @@ aas:ReferableShape a sh:NodeShape ;
sh:maxCount 1 ;
sh:minLength 1 ;
sh:maxLength 128 ;
sh:pattern "^[\\x09\\x0A\\x0D\\x20-\\uD7FF\\uE000-\\uFFFD\\U00010000-\\U0010FFFF]*$" ;
sh:pattern "^[\\x09\\x0a\\x0d\\x20-\\ud7ff\\ue000-\\ufffd\\U00010000-\\U0010ffff]*$" ;
sh:pattern "^[a-zA-Z][a-zA-Z0-9_]*$" ;
] ;
sh:property [
Expand Down Expand Up @@ -1159,7 +1159,7 @@ aas:ResourceShape a sh:NodeShape ;
sh:maxCount 1 ;
sh:minLength 1 ;
sh:maxLength 2000 ;
sh:pattern "^[\\x09\\x0A\\x0D\\x20-\\uD7FF\\uE000-\\uFFFD\\U00010000-\\U0010FFFF]*$" ;
sh:pattern "^[\\x09\\x0a\\x0d\\x20-\\ud7ff\\ue000-\\ufffd\\U00010000-\\U0010ffff]*$" ;
] ;
sh:property [
a sh:PropertyShape ;
Expand All @@ -1169,8 +1169,8 @@ aas:ResourceShape a sh:NodeShape ;
sh:maxCount 1 ;
sh:minLength 1 ;
sh:maxLength 100 ;
sh:pattern "^[\\x09\\x0A\\x0D\\x20-\\uD7FF\\uE000-\\uFFFD\\U00010000-\\U0010FFFF]*$" ;
sh:pattern "^([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+/([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+([ ]*;[ ]*([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+=(([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+|\"(([ !#-\\[\\]-~]|[\\x80-\\xff])|\\\\([ !-~]|[\\x80-\\xff]))*\"))*$" ;
sh:pattern "^[\\x09\\x0a\\x0d\\x20-\\ud7ff\\ue000-\\ufffd\\U00010000-\\U0010ffff]*$" ;
sh:pattern "^([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+/([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+([ \\t]*;[ \\t]*([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+=(([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+|\"(([\\t !#-\\[\\]-~]|[\\x80-\\xff])|\\\\([\\t !-~]|[\\x80-\\xff]))*\"))*$" ;
] ;
.

Expand All @@ -1185,7 +1185,7 @@ aas:SpecificAssetIdShape a sh:NodeShape ;
sh:maxCount 1 ;
sh:minLength 1 ;
sh:maxLength 64 ;
sh:pattern "^[\\x09\\x0A\\x0D\\x20-\\uD7FF\\uE000-\\uFFFD\\U00010000-\\U0010FFFF]*$" ;
sh:pattern "^[\\x09\\x0a\\x0d\\x20-\\ud7ff\\ue000-\\ufffd\\U00010000-\\U0010ffff]*$" ;
] ;
sh:property [
a sh:PropertyShape ;
Expand All @@ -1195,7 +1195,7 @@ aas:SpecificAssetIdShape a sh:NodeShape ;
sh:maxCount 1 ;
sh:minLength 1 ;
sh:maxLength 2000 ;
sh:pattern "^[\\x09\\x0A\\x0D\\x20-\\uD7FF\\uE000-\\uFFFD\\U00010000-\\U0010FFFF]*$" ;
sh:pattern "^[\\x09\\x0a\\x0d\\x20-\\ud7ff\\ue000-\\ufffd\\U00010000-\\U0010ffff]*$" ;
] ;
sh:property [
a sh:PropertyShape ;
Expand Down Expand Up @@ -1311,7 +1311,7 @@ aas:ValueReferencePairShape a sh:NodeShape ;
sh:maxCount 1 ;
sh:minLength 1 ;
sh:maxLength 2000 ;
sh:pattern "^[\\x09\\x0A\\x0D\\x20-\\uD7FF\\uE000-\\uFFFD\\U00010000-\\U0010FFFF]*$" ;
sh:pattern "^[\\x09\\x0a\\x0d\\x20-\\ud7ff\\ue000-\\ufffd\\U00010000-\\U0010ffff]*$" ;
] ;
sh:property [
a sh:PropertyShape ;
Expand Down

0 comments on commit f3bb96d

Please sign in to comment.