From 22a9705eab037d71d30649db7ef9410368471e46 Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Thu, 24 Jul 2025 18:53:17 +0200 Subject: [PATCH 01/11] fix: grammar according to RFC3987 Signed-off-by: Jan Kowalleck --- src/rfc3987_syntax/syntax_rfc3987.lark | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/src/rfc3987_syntax/syntax_rfc3987.lark b/src/rfc3987_syntax/syntax_rfc3987.lark index c1973c0..c69be59 100644 --- a/src/rfc3987_syntax/syntax_rfc3987.lark +++ b/src/rfc3987_syntax/syntax_rfc3987.lark @@ -57,8 +57,8 @@ ifragment: (ipchar | "/" | "?")* iunreserved: alpha | digit | "-" | "." | "_" | "~" | ucschar -ucschar: /[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]/ -iprivate: /[\uE000-\uF8FF]/ +ucschar: /[\u00A0-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF\U00010000-\U0001FFFD\U00020000-\U0002FFFD\U00030000-\U0003FFFD\U00040000-\U0004FFFD\U00050000-\U0005FFFD\U00060000-\U0006FFFD\U00070000-\U0007FFFD\U00080000-\U0008FFFD\U00090000-\U0009FFFD\U000A0000-\U000AFFFD\U000B0000-\U000BFFFD\U000C0000-\U000CFFFD\U000D0000-\U000DFFFD\U000E0000-\U000EFFFD]/ +iprivate: /[\uE000-\uF8FF\U000F0000-\U000FFFFD\U00100000-\U0010FFFD]/ sub_delims: "!" | "$" | "&" | "'" | "(" | ")" | "*" | "+" | "," | ";" | "=" @@ -67,15 +67,7 @@ ip_literal: "[" (ipv6address | ipvfuture) "]" ipvfuture: "v" hexdig+ "." (unreserved | sub_delims | ":")+ -ipv6address: h16 ":" h16 ":" h16 ":" h16 ":" h16 ":" h16 ":" ls32 - | "::" h16 ":" h16 ":" h16 ":" h16 ":" h16 ":" ls32 - | h16 "::" h16 ":" h16 ":" h16 ":" h16 ":" ls32 - | h16 ":" h16 "::" h16 ":" h16 ":" h16 ":" ls32 - | h16 ":" h16 ":" h16 "::" h16 ":" h16 ":" ls32 - | h16 ":" h16 ":" h16 ":" h16 "::" h16 ":" ls32 - | h16 ":" h16 ":" h16 ":" h16 ":" h16 "::" ls32 - | h16 ":" h16 ":" h16 ":" h16 ":" h16 ":" h16 "::" h16 - | h16 ":" h16 ":" h16 ":" h16 ":" h16 ":" h16 ":" h16 "::" +ipv6address: "TODO" h16: hexdig | hexdig hexdig From 917adfe1c1b49339424e84f34968b75bae72a600 Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Thu, 24 Jul 2025 18:57:55 +0200 Subject: [PATCH 02/11] fix: grammar according to RFC3987 Signed-off-by: Jan Kowalleck --- src/rfc3987_syntax/syntax_rfc3987.lark | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/src/rfc3987_syntax/syntax_rfc3987.lark b/src/rfc3987_syntax/syntax_rfc3987.lark index c69be59..365cf06 100644 --- a/src/rfc3987_syntax/syntax_rfc3987.lark +++ b/src/rfc3987_syntax/syntax_rfc3987.lark @@ -67,7 +67,15 @@ ip_literal: "[" (ipv6address | ipvfuture) "]" ipvfuture: "v" hexdig+ "." (unreserved | sub_delims | ":")+ -ipv6address: "TODO" +ipv6address: ( h16 ":" )~6 ls32 + | "::" ( h16 ":" )~3 ls32 + | h16? "::" ( h16 ":" )~4 ls32 + | ( ( h16 ":" )? h16 )? "::" ( h16 ":" )~3 ls32 + | ( ( h16 ":" )~0..2 h16 )? "::" ( h16 ":" )~2 ls32 + | ( ( h16 ":" )~0..3 h16 )? "::" h16 ":" ls32 + | ( ( h16 ":" )~0..4 h16 )? "::" ls32 + | ( ( h16 ":" )~0..5 h16 )? "::" h16 + | ( ( h16 ":" )~0..6 h16 )? "::" h16: hexdig | hexdig hexdig From ac70a09a0c98a5c92c412af60aeca2ec2ac4c5de Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Fri, 15 Aug 2025 17:10:44 +0200 Subject: [PATCH 03/11] tests: additional tests for ucschar, iprivate, IPv6address Signed-off-by: Jan Kowalleck --- tests/valid_syntax.json | 52 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/tests/valid_syntax.json b/tests/valid_syntax.json index 6f60090..e19b45b 100644 --- a/tests/valid_syntax.json +++ b/tests/valid_syntax.json @@ -108,10 +108,60 @@ "value": "пример/тест#часть2", "expect_lark": true, "reason": "" + }, + { + "value": "ucschar/\u00A0-\uD7FF/\uF900-\uFFCF/\uFDF0-\uFFEF", + "expect_lark": true, + "reason": "`ucschar` ranges: %xA0-D7FF / %xF900-FDCF / %xFDF0-FFEF" + }, + { + "value": "ucschar/\uD800\uDC00-\uD83F\uDFFD/\uD840\uDC00-\uD87F\uDFFD/\uD880\uDC00-\uD8BF\uDFFD", + "expect_lark": true, + "reason": "`ucschar` ranges: %x10000-1FFFD / %x20000-2FFFD / %x30000-3FFFD" + }, + { + "value": "ucschar/\uD8C0\uDC00-\uD8FF\uDFFD/\uD900\uDC00-\uD93F\uDFFD/\uD940\uDC00-\uD97F\uDFFD", + "expect_lark": true, + "reason": "`ucschar` ranges: %x40000-4FFFD / %x50000-5FFFD / %x60000-6FFFD" + }, + { + "value": "ucschar/\uD980\uDC00-\uD9BF\uDFFD/\uD9C0\uDC00-\uD9FF\uDFFD/\uDA00\uDC00-\uDA3F\uDFFD", + "expect_lark": true, + "reason": "`ucschar` ranges: %x70000-7FFFD / %x80000-8FFFD / %x90000-9FFFD" + }, + { + "value": "ucschar/\uDA40\uDC00-\uDA7F\uDFFD/\uDA80\uDC00-\uDABF\uDFFD/\uDAC0\uDC00-\uDAFF\uDFFD", + "expect_lark": true, + "reason": "`ucschar` ranges: %xA0000-AFFFD / %xB0000-BFFFD / %xC0000-CFFFD" + }, + { + "value": "ucschar/\uDB00\uDC00-\uDB3F\uDFFD/\uDB40\uDC00-\uDB7F\uDFFD", + "expect_lark": true, + "reason": "`ucschar` ranges: %xD0000-DFFFD / %xE0000-EFFFD" + }, + { + "value": "iprivate?foo=\uE000-\uF8FF&bar=\uDB80\uDC00-\uDBBF\uDFFD&baz=\uDBC0\uDC00-\uDBFF\uDFFD", + "expect_lark": true, + "reason": "`iprivate` ranges: %xE000-F8FF / %xF0000-FFFFD / %x100000-10FFFD" + }, + { + "value": "//[2345:0425:2CA1:0:0:0567:5673:23b5]/foo", + "expect_lark": true, + "reason": "`IPv6address` case: 6( h16 \":\" ) ls32" + }, + { + "value": "//[2345::BEEF]/foo", + "expect_lark": true, + "reason": "`IPv6address` case: [ *5( h16 \":\" ) h16 ] \"::\" h16" + }, + { + "value": "//[2041:0:140F::875B:131B]/foo", + "expect_lark": true, + "reason": "`IPv6address` case: [ *4( h16 \":\" ) h16 ] \"::\" ls32" } ], "reserved": [], "scheme": [], "pct_encoded": [], "ipv4address": [] -} \ No newline at end of file +} From fa0439b26b95504483e4d1d37474f7d772e456a9 Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Thu, 16 Jul 2026 10:42:33 +0200 Subject: [PATCH 04/11] tests Signed-off-by: Jan Kowalleck --- tests/valid_syntax.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/valid_syntax.json b/tests/valid_syntax.json index e19b45b..a6bba92 100644 --- a/tests/valid_syntax.json +++ b/tests/valid_syntax.json @@ -110,7 +110,7 @@ "reason": "" }, { - "value": "ucschar/\u00A0-\uD7FF/\uF900-\uFFCF/\uFDF0-\uFFEF", + "value": "ucschar/\u00A0-\uD7FF/\uF900-\uFDCF/\uFDF0-\uFFEF", "expect_lark": true, "reason": "`ucschar` ranges: %xA0-D7FF / %xF900-FDCF / %xFDF0-FFEF" }, From aec02374e0d7dec711bb22e6d300dc10837170ed Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Thu, 16 Jul 2026 10:44:15 +0200 Subject: [PATCH 05/11] docs Signed-off-by: Jan Kowalleck --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3964458..cb67e54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,11 @@ All notable changes to this project will be documented in this file. +* Tests + * Added more tests (via [#10]) + +[#10]: https://github.com/jkowalleck/rfc3987-syntax2/pull/10 + ## v1.1.1 - 2026-07-14 ### Fork release `rfc3987-syntax2` From e58a2e93fde4b597eeacf3e4508f84275c10e17b Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Thu, 16 Jul 2026 10:45:54 +0200 Subject: [PATCH 06/11] lark Signed-off-by: Jan Kowalleck --- src/rfc3987_syntax2/syntax_rfc3987.lark | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/rfc3987_syntax2/syntax_rfc3987.lark b/src/rfc3987_syntax2/syntax_rfc3987.lark index 365cf06..f0811e9 100644 --- a/src/rfc3987_syntax2/syntax_rfc3987.lark +++ b/src/rfc3987_syntax2/syntax_rfc3987.lark @@ -68,7 +68,7 @@ ip_literal: "[" (ipv6address | ipvfuture) "]" ipvfuture: "v" hexdig+ "." (unreserved | sub_delims | ":")+ ipv6address: ( h16 ":" )~6 ls32 - | "::" ( h16 ":" )~3 ls32 + | "::" ( h16 ":" )~5 ls32 | h16? "::" ( h16 ":" )~4 ls32 | ( ( h16 ":" )? h16 )? "::" ( h16 ":" )~3 ls32 | ( ( h16 ":" )~0..2 h16 )? "::" ( h16 ":" )~2 ls32 From 0b0486d6a5543f82a4dba28479c71f375464a3f9 Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Thu, 16 Jul 2026 12:43:10 +0200 Subject: [PATCH 07/11] docs Signed-off-by: Jan Kowalleck --- README.md | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index ae2a063..2bd6b24 100644 --- a/README.md +++ b/README.md @@ -15,13 +15,12 @@ Helper functions to parse and validate the **syntax** of terms defined in **[RFC The goal of `rfc3987-syntax2` is to provide a **lightweight, permissively licensed Python module** for validating that strings conform to the **ABNF grammar defined in RFC 3987**. These helpers are: -- ✅ Strictly aligned with the **syntax rules of RFC 3987** +- ✅ Strictly aligned with the **syntax rules of RFC 3987** (including grammar corrections reflected in current releases) - ✅ Built using a **permissive MIT license** - ✅ Designed for both **open source and proprietary use** - ✅ Powered by [Lark](https://github.com/lark-parser/lark), a fast, EBNF-based parser -> 🧠 **Note:** This project focuses on **syntax validation only**. RFC 3987 specifies **additional semantic rules** (e.g., Unicode normalization, BiDi constraints, percent-encoding requirements) that must be enforced separately. - +> 🧠 **Note:** This project focuses on **syntax validation only**. RFC 3987 specifies **additional semantic rules** (e.g., Unicode normalization, BiDi constraints, percent-encoding requirements) that are intentionally out of scope. ## 📄 License, Attribution, and Citation @@ -37,17 +36,16 @@ This project: ## ⚠️ Limitations -The grammar and parser enforce **only the ABNF syntax** defined in RFC 3987. The following are **not validated** and must be handled separately for full compliance: +The grammar and parser enforce **only the ABNF syntax** defined in RFC 3987 (and referenced RFC 3986 productions where applicable). The following are **not validated** and must be handled separately for full compliance: - ✅ Unicode **Normalization Form C (NFC)** - ✅ Bidirectional text (**BiDi**) constraints (RFC 3987 §4.1) - ✅ **Port number ranges** (must be 0–65535) -- ✅ Valid **IPv6 compression** (only one `::`, max segments) - ✅ Context-aware **percent-encoding** requirements +- ✅ Additional host/network semantics beyond ABNF syntax (for example, canonical representation expectations) ChatGPT 40 was used during the original development process. Errors may exist due to this assistance. Additional review, testing, and bug fixes by human experts is welcome. - ## 📦 Installation ```sh @@ -92,7 +90,7 @@ if is_valid_syntax_iri('http://github.com'): if not is_valid_syntax_iri('bob'): print("✗ Invalid IRI syntax") - + if is_valid_syntax_iri_reference('bob'): print("✓ Valid IRI-reference syntax") ``` @@ -115,7 +113,7 @@ This grammar was derived from: → Defines IRI syntax and extensions to URI (e.g. Unicode characters, `ucschar`) → https://datatracker.ietf.org/doc/html/rfc3987 -- **RFC 3986 – Uniform Resource Identifier (URI): Generic Syntax)** +- **RFC 3986 – Uniform Resource Identifier (URI): Generic Syntax** → Provides reusable components like `scheme`, `authority`, `ipv4address`, etc. → https://datatracker.ietf.org/doc/html/rfc3986 @@ -142,10 +140,10 @@ This grammar was derived from: | `ipchar`, `isegment` | RFC 3986 | Path characters and segments | | `isegment_nz_nc` | RFC 3987 | IRI-specific path constraint | | `iunreserved` | RFC 3987 | Includes `ucschar` | -| `ucschar`, `iprivate`| RFC 3987 | Unicode support | +| `ucschar`, `iprivate` | RFC 3987 | Unicode support | | `sub_delims` | RFC 3986 | Reserved characters | | `ip_literal` | RFC 3986 | IPv6 or IPvFuture in `[]` | -| `ipv6address` | RFC 3986 | Expanded forms only | +| `ipv6address` | RFC 3986 | RFC 3986 IPv6address production; syntax-level validation only | | `ipvfuture` | RFC 3986 | Forward-compatible | | `ipv4address` | RFC 3986 | Dotted-decimal IPv4 | | `ls32` | RFC 3986 | Final 32 bits of IPv6 | From f05b302ffb0038bb6c331507f083741e8c1dc6fa Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Thu, 16 Jul 2026 12:49:08 +0200 Subject: [PATCH 08/11] docs Signed-off-by: Jan Kowalleck --- CHANGELOG.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0d9f512..265e214 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,10 +6,17 @@ All notable changes to this project will be documented in this file. +## [Unreleased] + +* Fixed + * Align RFC 3987 grammar handling with the specification (via [#10]) + * Fix RFC 3986-delegated IRI parsing edge cases (via [#10]) +* Changed + * Update grammar rules used by `is_valid_syntax(...)` and term-specific validators (via [#10]) * Added * Assert static typing (via [#16]) * Tests - * Added more tests (via [#10]) + * Add grammar regression tests (via [#10]) [#10]: https://github.com/jkowalleck/rfc3987-syntax2/pull/10 [#16]: https://github.com/jkowalleck/rfc3987-syntax2/pull/16 From 8e3e6f094451641583fa11549c4fd1331ff91c94 Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Thu, 16 Jul 2026 12:56:14 +0200 Subject: [PATCH 09/11] docs Signed-off-by: Jan Kowalleck --- CHANGELOG.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 265e214..7e6bab4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,8 +6,6 @@ All notable changes to this project will be documented in this file. -## [Unreleased] - * Fixed * Align RFC 3987 grammar handling with the specification (via [#10]) * Fix RFC 3986-delegated IRI parsing edge cases (via [#10]) From 105b24f1363b5e86d06ea4abb08d9b1aa82f9acc Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Thu, 16 Jul 2026 13:02:03 +0200 Subject: [PATCH 10/11] Apply suggestions from code review Co-authored-by: Jan Kowalleck --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2bd6b24..395762f 100644 --- a/README.md +++ b/README.md @@ -15,7 +15,7 @@ Helper functions to parse and validate the **syntax** of terms defined in **[RFC The goal of `rfc3987-syntax2` is to provide a **lightweight, permissively licensed Python module** for validating that strings conform to the **ABNF grammar defined in RFC 3987**. These helpers are: -- ✅ Strictly aligned with the **syntax rules of RFC 3987** (including grammar corrections reflected in current releases) +- ✅ Strictly aligned with the **syntax rules of RFC 3987** - ✅ Built using a **permissive MIT license** - ✅ Designed for both **open source and proprietary use** - ✅ Powered by [Lark](https://github.com/lark-parser/lark), a fast, EBNF-based parser From f661bd3019a0a28e31ab59baa1bcc4f37280b720 Mon Sep 17 00:00:00 2001 From: Jan Kowalleck Date: Thu, 16 Jul 2026 13:04:02 +0200 Subject: [PATCH 11/11] docs Signed-off-by: Jan Kowalleck --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ffa37ce..bb58846 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ All notable changes to this project will be documented in this file. * Changed * Update grammar rules used by `is_valid_syntax(...)` and term-specific validators (via [#10]) * Added + * Validate IPv6 compression (via [#10]) * Assert static typing (via [#16]) * Tests * Add grammar regression tests (via [#10])