diff --git a/.github/workflows/python-publish.yml b/.github/workflows/python-publish.yml index dbaedd4..b413f04 100644 --- a/.github/workflows/python-publish.yml +++ b/.github/workflows/python-publish.yml @@ -10,30 +10,66 @@ name: Upload Python Package on: release: - types: [published] + types: [ published ] + +env: + PYTHON_VERSION_DEFAULT: "3.14" permissions: contents: read jobs: - deploy: - + quick-test: + name: QuickTest runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - name: Set up Python - uses: actions/setup-python@v3 - with: - python-version: '3.x' - - name: Install dependencies - run: | - python -m pip install --upgrade pip - pip install build - - name: Build package - run: python -m build - - name: Publish package - uses: pypa/gh-action-pypi-publish@27b31702a0e7fc50959f5ad993c78deac1bdfc29 - with: - user: __token__ - password: ${{ secrets.PYPI_API_TOKEN }} \ No newline at end of file + - name: Checkout + # see https://github.com/actions/checkout + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + - name: Setup Python Environment + # see https://github.com/actions/setup-python + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + with: + python-version: ${{ env.PYTHON_VERSION_DEFAULT }} + architecture: 'x64' + - name: Validate Python Environment + shell: python + run: | + import sys + print('Python %s on %s in %s' % (sys.version, sys.platform, sys.getdefaultencoding())) + - name: Install env dependencies + run: python -m pip install --upgrade pip + - name: Install project + run: python -m pip install --user -e ".[testing]" + - name: Run Tests + run: python -m pytest tests + publish: + name: Publish + needs: + - quick-test + runs-on: ubuntu-latest + permissions: + id-token: write # IMPORTANT: this permission is mandatory for trusted publishing + steps: + - name: Checkout + # see https://github.com/actions/checkout + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + - name: Setup Python Environment + # see https://github.com/actions/setup-python + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + with: + python-version: ${{ env.PYTHON_VERSION_DEFAULT }} + architecture: 'x64' + - name: Install env dependencies + run: | + python -m pip install --upgrade pip + python -m pip install build + - name: Build package + run: python -m build + - name: Publish package + # see https://github.com/pypa/gh-action-pypi-publish + uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 \ No newline at end of file diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml new file mode 100644 index 0000000..72d5a39 --- /dev/null +++ b/.github/workflows/python-tests.yml @@ -0,0 +1,79 @@ +# This workflow will upload a Python Package using Twine when a release is created +# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-python#publishing-to-package-registries + +# This workflow uses actions that are not certified by GitHub. +# They are provided by a third-party and are governed by +# separate terms of service, privacy policy, and support +# documentation. + + +name: Python CI + +on: + workflow_dispatch: + pull_request: + push: + tags: [ 'v*' ] # run again on release tags to have tools mark them + branches: [ 'main', 'next' ] + schedule: + # schedule daily tests, since some dependencies are not intended to be pinned + # this means: at 23:42 every day + - cron: '42 23 * * *' + +env: + PYTHON_VERSION_DEFAULT: "3.14" + +permissions: + contents: read + +jobs: + build-and-test: + name: Test (${{ matrix.os }} py${{ matrix.python-version }} ${{ matrix.toxenv-factors }}) + runs-on: ${{ matrix.os }} + timeout-minutes: 15 + strategy: + fail-fast: false + matrix: + os: + - ubuntu-latest + - macos-latest + - windows-latest + python-version: + - "3.14" # highest supported + - "3.13" + - "3.12" + - "3.11" + - "3.10" + - "3.9" # lowest supported + exclude: + - os: macos-latest # macos-latest is incompatible with some PY versions + python-version: "3.10" + - os: macos-latest # macos-latest is incompatible with some PY versions + python-version: "3.9" + steps: + - name: Disabled Git auto EOL CRLF transforms + run: | + git config --global core.autocrlf false + git config --global core.eol lf + - name: Checkout + # see https://github.com/actions/checkout + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + - name: Setup Python Environment + # see https://github.com/actions/setup-python + uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 + with: + python-version: ${{ matrix.python-version }} + architecture: 'x64' + - name: Validate Python Environment + shell: python + run: | + import sys + print('Python %s on %s in %s' % (sys.version, sys.platform, sys.getdefaultencoding())) + - name: Install env dependencies + run: python -m pip install --upgrade pip + - name: Install project + run: pip install --user -e ".[testing]" + - name: Run Tests + run: python -m pytest tests \ No newline at end of file diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml new file mode 100644 index 0000000..ba6c4f1 --- /dev/null +++ b/.github/workflows/zizmor.yml @@ -0,0 +1,44 @@ +# Analyzes all GitHub Actions workflows for security issues using zizmor. +# docs: https://docs.zizmor.sh/ +name: Zizmor + +on: + push: + branches: ['master', 'main'] + pull_request: + branches: ['**'] + workflow_dispatch: + schedule: + - cron: '0 0 * * 6' + +permissions: {} + +concurrency: + group: '${{ github.workflow }}-${{ github.ref }}' + cancel-in-progress: true + +jobs: + zizmor: + name: Zizmor + runs-on: ubuntu-latest + timeout-minutes: 10 + permissions: + contents: read + steps: + - name: Checkout + # see https://github.com/actions/checkout + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + with: + persist-credentials: false + - name: Run zizmor 🌈 + # see https://github.com/zizmorcore/zizmor-action + uses: zizmorcore/zizmor-action@192e21d79ab29983730a13d1382995c2307fbcaa # v0.5.7 + with: + # advanced-security: false => emit findings as workflow-command annotations (::error file=…) rather than + # uploading a SARIF report to GitHub's Security tab. + # Uploading SARIF requires `security-events: write` and GitHub Advanced Security (GHAS), + # both of which are unnecessary here and would violate the least-privilege policy. + # The two modes are mutually exclusive: advanced-security must be false for + # annotations to take effect. + advanced-security: false + annotations: true \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..3964458 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,31 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +## Unreleased + + + +## v1.1.1 - 2026-07-14 + +### Fork release `rfc3987-syntax2` +- Forked from [`willynilly/rfc3987-syntax`](https://github.com/willynilly/rfc3987-syntax). +- Published this project as an independently maintained package under the new distribution name `rfc3987-syntax2`. +- Preserved upstream functionality as the baseline for this initial fork release. + +### Notes for `rfc3987-syntax2` +- This is the first release of the fork. +- No intentional behavioral changes from upstream are introduced in this release unless explicitly listed above. + +## v1.1.0 - 2025-07-18 + +Original release: + +* Features + * Added add support for IRI-reference and absolute-IRI +* Bugs + * Fixed single quote sub-delimiter + +## v1.0.0 - 2025-05-18 + +Original release: \ No newline at end of file diff --git a/CITATION.cff b/CITATION.cff deleted file mode 100644 index f062222..0000000 --- a/CITATION.cff +++ /dev/null @@ -1,71 +0,0 @@ -cff-version: 1.2.0 -title: rfc3987-syntax -message: >- - If you use this software, please cite it using the - metadata from this file. -type: software -authors: - - given-names: Will - family-names: Riley - email: wanderingwill@gmail.com - orcid: "https://orcid.org/0000-0003-1822-6756" - - given-names: Jan - family-names: Kowalleck -repository-code: >- - https://github.com/willynilly/rfc3987-syntax -abstract: >- - Helper functions to syntactically validate strings according to RFC 3987 -keywords: - - RFC 3987 - - RFC3987 - - validator - - syntax - - parser -license: MIT -version: "1.1.0" -date-released: "2025-07-18" -references: - - title: "abnf-to-regexp" - type: software - version: "1.1.3" - license: MIT - authors: - - given-names: Marko - family-names: Ristin - email: marko@ristin.ch - orcid: "" - - given-names: Oliver Steensen-Bech - family-names: Haagh - email: oliver@dmc.international - orcid: "" - - given-names: Sebastian - family-names: Heppner - email: s.heppner@iat.rwth-aachen.de - orcid: "" - repository-code: https://github.com/aas-core-works/abnf-to-regexp - - title: "lark" - type: software - version: 1.2.2 - license: MIT - authors: - - family-names: Shinan - given-names: Erez - email: erezshin@gmail.com - repository-code: https://github.com/lark-parser/lark - - title: "Internationalized Resource Identifiers (IRIs)" - authors: - - family-names: DΓΌrst - given-names: Martin - - family-names: Suignard - given-names: Michel - date-released: 2005-01-01 - doi: "10.17487/RFC3987" - url: "https://www.rfc-editor.org/info/rfc3987" - type: standard - - title: "ChatGPT" - authors: - - name: OpenAI - type: software - version: "GPT-4o" - url: "https://chat.openai.com/chat" - \ No newline at end of file diff --git a/LICENSE b/LICENSE index dbc72c3..6efa3e6 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ MIT License Copyright (c) 2025 Will Riley +Copyright (c) 2026 Jan Kowalleck β€” modifications and maintenance. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 2e4d2e2..465ae59 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,13 @@ -# rfc3987-syntax +# rfc3987-syntax2 Helper functions to parse and validate the **syntax** of terms defined in **[RFC 3987](https://www.rfc-editor.org/info/rfc3987)** β€” the IETF standard for Internationalized Resource Identifiers (IRIs). +> [!NOTE] +> This is a maintained fork of . ## 🎯 Purpose -The goal of `rfc3987-syntax` 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: +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** - βœ… Built using a **permissive MIT license** @@ -17,7 +19,7 @@ The goal of `rfc3987-syntax` is to provide a **lightweight, permissively license ## πŸ“„ License, Attribution, and Citation -**`rfc3987-syntax`** is licensed under the [MIT License](LICENSE), which allows reuse in both open source and commercial software. +**`rfc3987-syntax2`** is licensed under the [MIT License](LICENSE), which allows reuse in both open source and commercial software. This project: @@ -45,8 +47,8 @@ ChatGPT 40 was used during the original development process. Errors may exist du ## πŸ“¦ Installation -```bash -pip install rfc3987-syntax +```sh +pip install rfc3987-syntax2 ``` ## πŸ›  Usage @@ -54,7 +56,7 @@ pip install rfc3987-syntax ### List all supported "terms" (i.e., non-terminals and terminals within ABNF production rules) used to validate the syntax of an IRI according to RFC 3987 ```python -from rfc3987_syntax import RFC3987_SYNTAX_TERMS +from rfc3987_syntax2 import RFC3987_SYNTAX_TERMS print("Supported terms:") for term in RFC3987_SYNTAX_TERMS: @@ -64,7 +66,7 @@ for term in RFC3987_SYNTAX_TERMS: ### Syntactically validate a string using the general-purpose validator ```python -from rfc3987_syntax import is_valid_syntax +from rfc3987_syntax2 import is_valid_syntax if is_valid_syntax(term='iri', value='http://github.com'): print("βœ“ Valid IRI syntax") @@ -79,8 +81,8 @@ if not is_valid_syntax(term='iri_reference', value='bob'): ### Alternatively, use term-specific helpers to validate RFC 3987 syntax. ```python -from rfc3987_syntax import is_valid_syntax_iri -from rfc3987_syntax import is_valid_syntax_iri_reference +from rfc3987_syntax2 import is_valid_syntax_iri +from rfc3987_syntax2 import is_valid_syntax_iri_reference if is_valid_syntax_iri('http://github.com'): print("βœ“ Valid IRI syntax") @@ -95,7 +97,7 @@ if is_valid_syntax_iri_reference('bob'): ### Get the Lark parse tree for a syntax validation (useful for additional semantic validation) ```python -from rfc3987_syntax import parse +from rfc3987_syntax2 import parse ptree: ParseTree = parse(term="iri", value="http://github.com") @@ -106,11 +108,11 @@ print(ptree) This grammar was derived from: -- **[RFC 3987 – Internationalized Resource Identifiers (IRIs)]** +- **RFC 3987 – Internationalized Resource Identifiers (IRIs)** β†’ 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 @@ -118,33 +120,33 @@ This grammar was derived from: ### Rule-to-Source Mapping -| Rule/Component | Source | Notes | -|----------------------|------------|-------| -| `iri` | RFC 3987 | Top-level IRI rule | -| `iri_reference` | RFC 3987 | Top-level IRI Reference rule | -| `absolute_iri` | RFC 3987 | Top-level Absolute IRI rule | -| `scheme` | RFC 3986 | Referenced by RFC 3987 Β§2.2 | -| `ihier_part` | RFC 3987 | IRI-specific hierarchy | -| `irelative_ref` | RFC 3987 | IRI-specific relative ref | -| `irelative_part` | RFC 3987 | IRI-specific relative part | -| `iauthority` | RFC 3986 | Standard URI authority | -| `ipath_abempty` | RFC 3986 | Path format variant | -| `ipath_absolute` | RFC 3986 | Absolute path | -| `ipath_noscheme` | RFC 3986 | Path disallowing scheme prefix | -| `ipath_rootless` | RFC 3986 | Used in non-scheme contexts | -| `iquery` | RFC 3987 | Query extension to URI | -| `ifragment` | RFC 3987 | Fragment extension to URI | -| `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 | -| `sub_delims` | RFC 3986 | Reserved characters | -| `ip_literal` | RFC 3986 | IPv6 or IPvFuture in `[]` | -| `ipv6address` | RFC 3986 | Expanded forms only | -| `ipvfuture` | RFC 3986 | Forward-compatible | -| `ipv4address` | RFC 3986 | Dotted-decimal IPv4 | -| `ls32` | RFC 3986 | Final 32 bits of IPv6 | -| `h16`, `dec_octet` | RFC 3986 | Hex and decimal chunks | -| `port` | RFC 3986 | Optional numeric | -| `pct_encoded` | RFC 3986 | Percent encoding (e.g. `%20`) | +| Rule/Component | Source | Notes | +|----------------|--------|-------| +| `iri` | RFC 3987 | Top-level IRI rule | +| `iri_reference` | RFC 3987 | Top-level IRI Reference rule | +| `absolute_iri` | RFC 3987 | Top-level Absolute IRI rule | +| `scheme` | RFC 3986 | Referenced by RFC 3987 Β§2.2 | +| `ihier_part` | RFC 3987 | IRI-specific hierarchy | +| `irelative_ref` | RFC 3987 | IRI-specific relative ref | +| `irelative_part` | RFC 3987 | IRI-specific relative part | +| `iauthority` | RFC 3986 | Standard URI authority | +| `ipath_abempty` | RFC 3986 | Path format variant | +| `ipath_absolute` | RFC 3986 | Absolute path | +| `ipath_noscheme` | RFC 3986 | Path disallowing scheme prefix | +| `ipath_rootless` | RFC 3986 | Used in non-scheme contexts | +| `iquery` | RFC 3987 | Query extension to URI | +| `ifragment` | RFC 3987 | Fragment extension to URI | +| `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 | +| `sub_delims` | RFC 3986 | Reserved characters | +| `ip_literal` | RFC 3986 | IPv6 or IPvFuture in `[]` | +| `ipv6address` | RFC 3986 | Expanded forms only | +| `ipvfuture` | RFC 3986 | Forward-compatible | +| `ipv4address` | RFC 3986 | Dotted-decimal IPv4 | +| `ls32` | RFC 3986 | Final 32 bits of IPv6 | +| `h16`, `dec_octet` | RFC 3986 | Hex and decimal chunks | +| `port` | RFC 3986 | Optional numeric | +| `pct_encoded` | RFC 3986 | Percent encoding (e.g. `%20`) | | `alpha`, `digit`, `hexdig` | RFC 3986 | Character classes | diff --git a/pyproject.toml b/pyproject.toml index e0e1c16..d810a91 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,8 +3,8 @@ requires = ["hatchling"] build-backend = "hatchling.build" [project] -name = "rfc3987-syntax" -version = "1.1.0" +name = "rfc3987-syntax2" +version = "1.1.1" description = "Helper functions to syntactically validate strings according to RFC 3987." readme = "README.md" requires-python = ">=3.9" @@ -12,21 +12,21 @@ license = "MIT" license-files = ["LICEN[CS]E*"] keywords = ["RFC 3987", "RFC3987", "validator", "syntax", "parser"] authors = [ - { name = "Will Riley", email = "wanderingwill@gmail.com" }, - { name = "Jan Kowalleck" } + { name = "Jan Kowalleck" }, + { name = "Will Riley", email = "wanderingwill@gmail.com" } ] classifiers = [ + # ALPHA - not all features of RFC 3987 are implemented, yet "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "Intended Audience :: Education", "Intended Audience :: Information Technology", "Intended Audience :: Science/Research", "Intended Audience :: System Administrators", - "License :: OSI Approved :: Apache Software License", + "License :: OSI Approved :: MIT License", "Natural Language :: English", "Programming Language :: Python", "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.9", "Topic :: Scientific/Engineering", "Topic :: Software Development", "Topic :: Utilities" @@ -34,10 +34,10 @@ classifiers = [ dependencies = ["lark>=1.2.2"] [project.urls] -Homepage = "https://github.com/willynilly/rfc3987-syntax" -Documentation = "https://github.com/willynilly/rfc3987-syntax#readme" -Issues = "https://github.com/willynilly/rfc3987-syntax/issues" -Source = "https://github.com/willynilly/rfc3987-syntax" +Homepage = "https://github.com/jkowalleck/rfc3987-syntax2" +Documentation = "https://github.com/jkowalleck/rfc3987-syntax2#readme" +Issues = "https://github.com/jkowalleck/rfc3987-syntax2/issues" +Source = "https://github.com/jkowalleck/rfc3987-syntax2" [project.optional-dependencies] testing = [ @@ -45,9 +45,17 @@ testing = [ ] [tool.pytest.ini_options] -pythonpath = [ - "src" +pythonpath = ["src"] + +[tool.hatch.build.targets.sdist] +exclude = [ + "/.*", + "/_*", +] +include = [ + "/src", + "/tests" ] -[tool.hatch.build.targets.sdist.force-include] -"CITATION.cff" = "src/rfc3987_syntax/CITATION.cff" \ No newline at end of file +[tool.hatch.build.targets.wheel] +packages = ["src/rfc3987_syntax2"] diff --git a/src/rfc3987_syntax/__init__.py b/src/rfc3987_syntax2/__init__.py similarity index 100% rename from src/rfc3987_syntax/__init__.py rename to src/rfc3987_syntax2/__init__.py diff --git a/src/rfc3987_syntax/syntax_helpers.py b/src/rfc3987_syntax2/syntax_helpers.py similarity index 98% rename from src/rfc3987_syntax/syntax_helpers.py rename to src/rfc3987_syntax2/syntax_helpers.py index fcb3f32..117a2ce 100644 --- a/src/rfc3987_syntax/syntax_helpers.py +++ b/src/rfc3987_syntax2/syntax_helpers.py @@ -2,7 +2,7 @@ from pathlib import Path -from rfc3987_syntax.utils import load_grammar +from .utils import load_grammar RFC3987_SYNTAX_PARSER_TYPE: str = "earley" RFC3987_SYNTAX_GRAMMAR_PATH: Path = Path(__file__).parent / "syntax_rfc3987.lark" diff --git a/src/rfc3987_syntax/syntax_rfc3987.lark b/src/rfc3987_syntax2/syntax_rfc3987.lark similarity index 100% rename from src/rfc3987_syntax/syntax_rfc3987.lark rename to src/rfc3987_syntax2/syntax_rfc3987.lark diff --git a/src/rfc3987_syntax/utils.py b/src/rfc3987_syntax2/utils.py similarity index 100% rename from src/rfc3987_syntax/utils.py rename to src/rfc3987_syntax2/utils.py diff --git a/tests/test_syntax.py b/tests/test_syntax.py index d71923c..4974ae2 100644 --- a/tests/test_syntax.py +++ b/tests/test_syntax.py @@ -1,8 +1,9 @@ from pathlib import Path -import rfc3987_syntax as h import json import pytest +import rfc3987_syntax2 as sut + @pytest.fixture def valid_syntax_data(): @@ -19,13 +20,13 @@ def invalid_syntax_data(): def test_is_valid_syntax(valid_syntax_data): for term, valid_examples in valid_syntax_data.items(): for valid_example in valid_examples: - actual = h.is_valid_syntax(term=term, value=valid_example["value"]) + actual = sut.is_valid_syntax(term=term, value=valid_example["value"]) print("") print( f"Testing {term} with {valid_example['value']} : {valid_example['reason']}" ) assert ( - actual is True + actual is True ), f"Failed term: {term} for '{valid_example['value']}' : {valid_example['reason']}" assert True @@ -33,7 +34,7 @@ def test_is_valid_syntax(valid_syntax_data): def test_not_is_valid_syntax(invalid_syntax_data): for term, invalid_examples in invalid_syntax_data.items(): for invalid_example in invalid_examples: - actual = h.is_valid_syntax(term=term, value=invalid_example["value"]) + actual = sut.is_valid_syntax(term=term, value=invalid_example["value"]) print("") print( f"Testing {term} with {invalid_example['value']} : {invalid_example['reason']}" 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 +}