From f242250d2214a6942f385fd23c96769a2479473f Mon Sep 17 00:00:00 2001 From: Lars Willighagen Date: Thu, 9 Sep 2021 16:50:58 +0200 Subject: [PATCH 1/4] draft Schema spec for node and value tags (#135) --- SCHEMA-SPEC.md | 26 ++++++++++++++++++++++++++ examples/kdl-schema.kdl | 41 +++++++++++++++++++++++++++++++++++++++-- 2 files changed, 65 insertions(+), 2 deletions(-) diff --git a/SCHEMA-SPEC.md b/SCHEMA-SPEC.md index 6a6e20d..05c0965 100644 --- a/SCHEMA-SPEC.md +++ b/SCHEMA-SPEC.md @@ -33,6 +33,9 @@ None. * [`definitions`](#definitions-node) (optional): Definitions of nodes, values, props, and children block to reference in the toplevel nodes. * `node-names` (optional): [Validations](#validation-nodes) to apply to the _names_ of child nodes. * `other-nodes-allowed` (optional): Whether to allow nodes other than the ones explicitly listed here. Defaults to `false`. +* [`tag`](#tag-node) - zero or more toplevel tags for nodes in the KDL document that this schema describes. +* `tag-names` (optional): [Validations](#validation-nodes) to apply to the _names_ of tags of child nodes. +* `other-tags-allowed` (optional): Whether to allow node tags other than the ones explicitly listed here. Defaults to `false`. ### `info` node @@ -169,10 +172,31 @@ another node. * `max` (optional): Maximum number of this kind of node (or any node, if the name is missing) allowed in the parent's children block. * `prop-names` (optional): [Validations](#validation-nodes) to apply to the _names_ of properties. * `other-props-allowed` (optional): Whether to allow props other than the ones explicitly listed here. Defaults to `false`. +* `tag`: [Validations](#validation-nodes) to apply to the tag of the node. * [`prop`](#prop-node) - zero or more properties for this node. * [`value`](#value-node) - zero or more values for this node. * [`children`](#children-node) - zero or more children for this node. +### `tag` node + +The `tag` describes the tags allowed in a children block or toplevel document. + +#### Values + +* Tag name (optional) - A tag for the node. If present, the node's rules/validations will apply only to children with this tag. Otherwise, the rules will apply to _all_ child nodes with tags. + +#### Properties + +* `description` (optional): An informational description of the purpose of this node. +* `id` (optional): A globally unique identifier for this node. +* `ref` (optional): A globally unique reference to another node's ID. If present, all properties, values, and children defined in the target node will be copied to this node, replacing any conflicts. + +#### Children + +* [`node`](#node-node) - zero or more toplevel nodes that this tag is allowed to be on. +* `node-names` (optional): [Validations](#validation-nodes) to apply to the _names_ of nodes with this tag. +* `other-nodes-allowed` (optional): Whether to allow nodes other than the ones explicitly listed here. Defaults to `false`. + ### `prop` node Represents a property of a node, which is a key/value pair in KDL. @@ -240,6 +264,7 @@ and property names when the `node-names` or `prop-names` options are activated. #### Generic validations +* `tag`: [Validations](#validation-nodes) to apply to the tag of the value. * `type`: A string denoting the type of the property value. * `enum`: A specific list of allowed values for this property. May be heterogenous as long as it agrees with the `type`, if specified. @@ -290,6 +315,7 @@ None. #### Children * [`node`](#node-node) - zero or more node definitions. +* [`tag`](#tag-node) - zero or more toplevel tags for nodes in the KDL document that this schema describes. * [`prop`](#prop-node) - zero or more property definitions. * [`value`](#value-node) - zero or more value definitions. * [`children`](#children-node) - zero or more definitions of children blocks. diff --git a/examples/kdl-schema.kdl b/examples/kdl-schema.kdl index b514b39..d398cd1 100644 --- a/examples/kdl-schema.kdl +++ b/examples/kdl-schema.kdl @@ -19,10 +19,21 @@ document { min 1 max 1 children id="node-children" { - node "node-names" description="Validations to apply specifically to arbitrary node names" { + node "node-names" id="node-names-node" description="Validations to apply specifically to arbitrary node names" { children ref="#validations" } - node "other-nodes-allowed" description="Whether to allow child nodes other than the ones explicitly listed. Defaults to 'false'." { + node "other-nodes-allowed" id="other-nodes-allowed-node" description="Whether to allow child nodes other than the ones explicitly listed. Defaults to 'false'." { + max 1 + value { + min 1 + max 1 + type "boolean" + } + } + node "tag-names" description="Validations to apply specifically to arbitrary type tag names" { + children ref="#validations" + } + node "other-tags-allowed" description="Whether to allow child node tags other than the ones explicitly listed. Defaults to 'false'." { max 1 value { min 1 @@ -125,6 +136,26 @@ document { } } } + node "tag" id="tag-node" description="A tag belonging to a child node of `document` or another node." { + value description="The name of the tag. If a tag name is not supplied, the node rules apply to _all_ nodes belonging to the parent." { + type "string" + max 1 + } + prop "description" description="A description of this node's purpose." { + type "string" + } + prop "id" description="A globally-unique ID for this node." { + type "string" + } + prop "ref" description="A globally unique reference to another node." { + type "string" + } + children { + node ref="#node-names-node" + node ref="#other-nodes-allowed-node" + node ref="#node-node" + } + } node "node" id="node-node" description="A child node belonging either to `document` or to another `node`. Nodes may be anonymous." { value description="The name of the node. If a node name is not supplied, the node rules apply to _all_ nodes belonging to the parent." { type "string" @@ -167,6 +198,7 @@ document { type "number" } } + node ref="#value-tag-node" node "prop" id="prop-node" description="A node property key/value pair." { value description="The property key." { type "string" @@ -191,6 +223,10 @@ document { } } children id="validations" description="General value validations." { + node "tag" id="value-tag-node" description="The tags associated with this value" { + max 1 + children ref="#validations" + } node "type" description="The type for this prop's value." { max 1 value { @@ -324,6 +360,7 @@ document { node ref="#value-node" node ref="#prop-node" node ref="#children-node" + node ref="#tag-node" } } } From 0e898780720bbfd7ff8c05f3dd11bac962b2f3ae Mon Sep 17 00:00:00 2001 From: Lars Willighagen Date: Thu, 9 Sep 2021 19:03:07 +0200 Subject: [PATCH 2/4] Add tag syntax to KQL (#137) --- QUERY-SPEC.md | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/QUERY-SPEC.md b/QUERY-SPEC.md index bc7f857..e51b6b9 100644 --- a/QUERY-SPEC.md +++ b/QUERY-SPEC.md @@ -24,12 +24,14 @@ lack of `*` (use `[]` instead), and the specific syntax for ## Matchers Matchers are used to filter nodes by their various attributes (such as values, -properties, node names, etc). With the exception of `top()`, they are all used -inside a `[ ]` selector. Some matchers are unary, but most of them involve +properties, node names, etc). With the exception of `top()` and `()`, they are all +used inside a `[ ]` selector. Some matchers are unary, but most of them involve binary operators. * `top()`: Returns all toplevel children of the current document. * `top() > []`: Equivalent to `top()` on its own. +* `(foo)`: Selects any element with a tag named `foo`. +* `()`: Selects any element with any tag. * `[val()]`: Selects any element with a value. * `[val(1)]`: Selects any element with a second value. * `[prop(foo)]`: Selects any element with a property named `foo`. @@ -41,9 +43,10 @@ Attribute matchers support certain binary operators: * `[prop(name) = 1]`: Selects any element with a property `name` whose value is 1. * `[name = 1]`: Equivalent to the above. * `[name() = "hi"]`: Selects any element whose _node name_ is "hi". Equivalent to just `hi`, but more useful when using string operators. +* `[tag() = "hi"]`: Selects any element whose tag is "hi". Equivalent to just `(hi)`, but more useful when using string operators. * `[val() != 1]`: Selects any element whose first value exists, and is not 1. -The following operators work with any `val()`, `prop()`, or `name()` values. +The following operators work with any `val()` or `prop()` values. If the value is not of the same type, the operator will always fail ("1" is never coerced to 1, and there is no "universal" ordering across all types.): @@ -52,12 +55,18 @@ never coerced to 1, and there is no "universal" ordering across all types.): * `[val() < 1]`: Selects any element whose first value is less than 1. * `[val() <= 1]`: Selects any element whose first value is less than or equal to 1. -The following operators work only with string `val()`, `prop()`, or `name()` values. If the value is not a string, the matcher will always fail: +The following operators work only with string `val()`, `prop()`, `tag()`, or `name()` values. +If the value is not a string, the matcher will always fail: * `[val() ^= "foo"]`: Selects any element whose first value starts with "foo". * `[val() $= "foo"]`: Selects any element whose first value ends with "foo". * `[val() *= "foo"]`: Selects any element whose first value contains "foo". +The following operators work only with `val()` or `prop()` values. If the value +is not one of those, the matcher will always fail: + +* `[val() = (foo)]`: Selects any element whose tag is "foo". + ## Map Operator KQL implementations MAY support a "map operator", `=>`, that allows selection From 5a13b920c422f9703c892a328be8e89494e00b39 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kat=20March=C3=A1n?= Date: Thu, 9 Sep 2021 19:03:38 -0700 Subject: [PATCH 3/4] use KDL Query for refs (#136) --- SCHEMA-SPEC.md | 13 ++++++----- examples/kdl-schema.kdl | 51 ++++++++++++++++++++++------------------- 2 files changed, 35 insertions(+), 29 deletions(-) diff --git a/SCHEMA-SPEC.md b/SCHEMA-SPEC.md index 05c0965..42d7433 100644 --- a/SCHEMA-SPEC.md +++ b/SCHEMA-SPEC.md @@ -164,7 +164,7 @@ another node. * `description` (optional): An informational description of the purpose of this node. * `id` (optional): A globally unique identifier for this node. -* `ref` (optional): A globally unique reference to another node's ID. If present, all properties, values, and children defined in the target node will be copied to this node, replacing any conflicts. +* `ref` (optional): A [KDL Query](./QUERY-SPEC.md) string relative to the root of the document. If present, all properties, values, and children defined in the target node will be copied to this node, replacing any conflicts. #### Children @@ -172,7 +172,7 @@ another node. * `max` (optional): Maximum number of this kind of node (or any node, if the name is missing) allowed in the parent's children block. * `prop-names` (optional): [Validations](#validation-nodes) to apply to the _names_ of properties. * `other-props-allowed` (optional): Whether to allow props other than the ones explicitly listed here. Defaults to `false`. -* `tag`: [Validations](#validation-nodes) to apply to the tag of the node. +* `tag`: [Validations](#validation-nodes) to apply to the tag of the node. * [`prop`](#prop-node) - zero or more properties for this node. * [`value`](#value-node) - zero or more values for this node. * [`children`](#children-node) - zero or more children for this node. @@ -189,7 +189,7 @@ The `tag` describes the tags allowed in a children block or toplevel document. * `description` (optional): An informational description of the purpose of this node. * `id` (optional): A globally unique identifier for this node. -* `ref` (optional): A globally unique reference to another node's ID. If present, all properties, values, and children defined in the target node will be copied to this node, replacing any conflicts. +* `ref` (optional): A [KDL Query](./QUERY-SPEC.md) string relative to the root of the document. If present, all properties, values, and children defined in the target node will be copied to this node, replacing any conflicts. #### Children @@ -209,7 +209,7 @@ Represents a property of a node, which is a key/value pair in KDL. * `description` (optional): An informational description of the purpose of this property. * `id` (optional): A globally unique identifier for this property. -* `ref` (optional): A globally unique reference to another property's ID. If present, all properties defined in the target property will be copied to this property, replacing any conflicts. +* `ref` (optional): A [KDL Query](./QUERY-SPEC.md) string relative to the root of the document. If present, all properties defined in the target property will be copied to this property, replacing any conflicts. #### Children @@ -228,7 +228,7 @@ None. * `description` (optional): An informational description of the purpose of this value. * `id` (optional): A globally unique identifier for this value. -* `ref` (optional): A globally unique reference to another value's ID. If present, all values defined in the target value will be copied to this value, replacing any conflicts. +* `ref` (optional): A [KDL Query](./QUERY-SPEC.md) string relative to the root of the document. If present, all values defined in the target value will be copied to this value, replacing any conflicts. #### Children @@ -248,7 +248,7 @@ None. * `description` (optional): An informational description of the purpose of this children block. * `id` (optional): A globally unique identifier for this children block. -* `ref` (optional): A globally unique reference to another children block's ID. If present, all children defined in the target children block will be copied to this children block, replacing any conflicts. +* `ref` (optional): A [KDL Query](./QUERY-SPEC.md) string relative to the root of the document. If present, all children defined in the target children block will be copied to this children block, replacing any conflicts. #### Children @@ -291,6 +291,7 @@ and property names when the `node-names` or `prop-names` options are activated. * `uuid`: RFC4122 UUID. * `regex`: Regular expression. Specific patterns may be implementation-dependent. * `base64`: A Base64-encoded string, denoting arbitrary binary data. + * `kdl-query`: A [KDL Query](./QUERY-SPEC.md) string. #### Number validations diff --git a/examples/kdl-schema.kdl b/examples/kdl-schema.kdl index d398cd1..b9c11df 100644 --- a/examples/kdl-schema.kdl +++ b/examples/kdl-schema.kdl @@ -20,7 +20,7 @@ document { max 1 children id="node-children" { node "node-names" id="node-names-node" description="Validations to apply specifically to arbitrary node names" { - children ref="#validations" + children ref=r#"[id="validations"]"# } node "other-nodes-allowed" id="other-nodes-allowed-node" description="Whether to allow child nodes other than the ones explicitly listed. Defaults to 'false'." { max 1 @@ -31,7 +31,7 @@ document { } } node "tag-names" description="Validations to apply specifically to arbitrary type tag names" { - children ref="#validations" + children ref=r#"[id="validations"]"# } node "other-tags-allowed" description="Whether to allow child node tags other than the ones explicitly listed. Defaults to 'false'." { max 1 @@ -59,7 +59,7 @@ document { min 1 max 1 } - prop ref="#info-lang" + prop ref=r#"[id="info-lang"]"# } node "author" description="Author of the schema" { value id="info-person-name" description="Person name" { @@ -72,12 +72,12 @@ document { pattern r"\d{4}-\d{4}-\d{4}-\d{4}" } children { - node ref="#info-link" + node ref=r#"[id="info-link"]"# } } node "contributor" description="Contributor to the schema" { - value ref="#info-person-name" - prop ref="#info-orcid" + value ref=r#"[id="info-person-name"]"# + prop ref=r#"[id="info-orcid"]"# } node "link" id="info-link" description="Links to itself, and to sources describing it" { value description="A URL that the link points to" { @@ -90,7 +90,7 @@ document { type "string" enum "self" "documentation" } - prop ref="#info-lang" + prop ref=r#"[id="info-lang"]"# } node "license" description="The license(s) that the schema is licensed under" { value description="Name of the used license" { @@ -102,7 +102,7 @@ document { type "string" } children { - node ref="#info-link" + node ref=r#"[id="info-link"]"# } } node "published" description="When the schema was published" { @@ -124,7 +124,7 @@ document { min 1 max 1 } - prop ref="#info-time" + prop ref=r#"[id="info-time"]"# } node "version" description="The version number of this version of the schema" { value description="Semver version number" { @@ -149,11 +149,12 @@ document { } prop "ref" description="A globally unique reference to another node." { type "string" + format "kdl-query" } children { - node ref="#node-names-node" - node ref="#other-nodes-allowed-node" - node ref="#node-node" + node ref=r#"[id="node-names-node"]"# + node ref=r#"[id="other-nodes-allowed-node"]"# + node ref=r#"[id="node-node"]"# } } node "node" id="node-node" description="A child node belonging either to `document` or to another `node`. Nodes may be anonymous." { @@ -169,10 +170,11 @@ document { } prop "ref" description="A globally unique reference to another node." { type "string" + format "kdl-query" } children { node "prop-names" description="Validations to apply specifically to arbitrary property names" { - children ref="#validations" + children ref=r#"[id="validations"]"# } node "other-props-allowed" description="Whether to allow properties other than the ones explicitly listed. Defaults to 'false'." { max 1 @@ -198,7 +200,7 @@ document { type "number" } } - node ref="#value-tag-node" + node ref=r#"[id="value-tag-node"]"# node "prop" id="prop-node" description="A node property key/value pair." { value description="The property key." { type "string" @@ -208,6 +210,7 @@ document { } prop "ref" description="A globally unique reference to another property node." { type "string" + format "kdl-query" } prop "description" description="A description of this property's purpose." { type "string" @@ -225,7 +228,7 @@ document { children id="validations" description="General value validations." { node "tag" id="value-tag-node" description="The tags associated with this value" { max 1 - children ref="#validations" + children ref="[id="validations"]" } node "type" description="The type for this prop's value." { max 1 @@ -266,7 +269,7 @@ document { min 1 type "string" // https://json-schema.org/understanding-json-schema/reference/string.html#format - enum "date-time" "date" "time" "email" "idn-email" "hostname" "idn-hostname" "ipv4" "ipv6" "uri" "uri-reference" "iri", "iri-reference" "uri-template" "regex" "uuid" + enum "date-time" "date" "time" "email" "idn-email" "hostname" "idn-hostname" "ipv4" "ipv6" "uri" "uri-reference" "iri", "iri-reference" "uri-template" "regex" "uuid" "kdl-query" } } node "%" description="Only used for numeric values. Constrains them to be multiples of the given number(s)" { @@ -316,11 +319,12 @@ document { } prop "ref" description="A globally unique reference to another value node." { type "string" + format "kdl-query" } prop "description" description="A description of this property's purpose." { type "string" } - children ref="#validations" + children ref=r#"[id="validations"]"# children description="Node value-specific validations" { node "min" description="minimum number of values for this node." { max 1 @@ -346,21 +350,22 @@ document { } prop "ref" description="A globally unique reference to another children node." { type "string" + format "kdl-query" } prop "description" description="A description of this these children's purpose." { type "string" } - children ref="#node-children" + children ref=r#"[id="node-children"]"# } } } node "definitions" description="Definitions to reference in parts of the top-level nodes" { children { - node ref="#node-node" - node ref="#value-node" - node ref="#prop-node" - node ref="#children-node" - node ref="#tag-node" + node ref=r#"[id="node-node"]"# + node ref=r#"[id="value-node"]"# + node ref=r#"[id="prop-node"]"# + node ref=r#"[id="children-node"]"# + node ref=r#"[id="tag-node"]"# } } } From 0cdda0b711aa8ed52c1ed870cc6ec81cbec89c7b Mon Sep 17 00:00:00 2001 From: Lars Willighagen Date: Sat, 11 Sep 2021 02:11:41 +0200 Subject: [PATCH 4/4] Disallow ws after (type) in grammar (#138) Fixes: https://github.com/kdl-org/kdl/issues/131 --- SPEC.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SPEC.md b/SPEC.md index 5512009..f0443d7 100644 --- a/SPEC.md +++ b/SPEC.md @@ -419,7 +419,7 @@ bare-identifier := ((identifier-char - digit - sign) identifier-char* | sign ((i identifier-char := unicode - linespace - [\/(){}<>;[]=,"] keyword := boolean | 'null' prop := identifier '=' value -value := (type ws*)? (string | number | keyword) +value := type? (string | number | keyword) type := '(' identifier ')' string := raw-string | escaped-string