sel-lang 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. sel_lang-0.3.0/.gitignore +101 -0
  2. sel_lang-0.3.0/LICENSE +36 -0
  3. sel_lang-0.3.0/PACKAGING.md +288 -0
  4. sel_lang-0.3.0/PKG-INFO +735 -0
  5. sel_lang-0.3.0/README.md +707 -0
  6. sel_lang-0.3.0/conformance/01-lexical.selt +158 -0
  7. sel_lang-0.3.0/conformance/02-numbers.selt +299 -0
  8. sel_lang-0.3.0/conformance/03-operators.selt +224 -0
  9. sel_lang-0.3.0/conformance/04-values.selt +257 -0
  10. sel_lang-0.3.0/conformance/05-control.selt +232 -0
  11. sel_lang-0.3.0/conformance/06-aggregates.selt +145 -0
  12. sel_lang-0.3.0/conformance/07-text.selt +259 -0
  13. sel_lang-0.3.0/conformance/08-binary.selt +162 -0
  14. sel_lang-0.3.0/conformance/09-regex.selt +440 -0
  15. sel_lang-0.3.0/conformance/10-limits.selt +99 -0
  16. sel_lang-0.3.0/conformance/11-arity.selt +641 -0
  17. sel_lang-0.3.0/conformance/12-misuse.selt +1047 -0
  18. sel_lang-0.3.0/conformance/README.md +135 -0
  19. sel_lang-0.3.0/docs/EXTENDING.md +878 -0
  20. sel_lang-0.3.0/docs/LANGUAGE.md +726 -0
  21. sel_lang-0.3.0/docs/PARSER-MIGRATION.md +210 -0
  22. sel_lang-0.3.0/examples/e2e.py +87 -0
  23. sel_lang-0.3.0/examples/host-python.py +98 -0
  24. sel_lang-0.3.0/examples/order-validation.sel +25 -0
  25. sel_lang-0.3.0/pyproject.toml +73 -0
  26. sel_lang-0.3.0/python/bin/api.py +126 -0
  27. sel_lang-0.3.0/python/bin/batch.py +97 -0
  28. sel_lang-0.3.0/python/bin/check-decimal.py +85 -0
  29. sel_lang-0.3.0/python/bin/conformance.py +268 -0
  30. sel_lang-0.3.0/python/sel/__init__.py +132 -0
  31. sel_lang-0.3.0/python/sel/__main__.py +6 -0
  32. sel_lang-0.3.0/python/sel/_cli.py +85 -0
  33. sel_lang-0.3.0/python/sel/builtins/__init__.py +15 -0
  34. sel_lang-0.3.0/python/sel/builtins/aggregate.py +110 -0
  35. sel_lang-0.3.0/python/sel/builtins/binary.py +149 -0
  36. sel_lang-0.3.0/python/sel/builtins/control.py +43 -0
  37. sel_lang-0.3.0/python/sel/builtins/number.py +52 -0
  38. sel_lang-0.3.0/python/sel/builtins/regex.py +462 -0
  39. sel_lang-0.3.0/python/sel/builtins/structure.py +9 -0
  40. sel_lang-0.3.0/python/sel/builtins/text.py +171 -0
  41. sel_lang-0.3.0/python/sel/decimal.py +226 -0
  42. sel_lang-0.3.0/python/sel/errors.py +40 -0
  43. sel_lang-0.3.0/python/sel/eval.py +408 -0
  44. sel_lang-0.3.0/python/sel/lexer.py +333 -0
  45. sel_lang-0.3.0/python/sel/parser.py +388 -0
  46. sel_lang-0.3.0/python/sel/py.typed +0 -0
  47. sel_lang-0.3.0/python/sel/registry.py +50 -0
  48. sel_lang-0.3.0/python/sel/utf8.py +126 -0
  49. sel_lang-0.3.0/python/sel/value.py +336 -0
  50. sel_lang-0.3.0/python/tests/test_unit.py +296 -0
  51. sel_lang-0.3.0/spec/SPEC.md +753 -0
  52. sel_lang-0.3.0/spec/errors.md +85 -0
  53. sel_lang-0.3.0/spec/grammar.md +137 -0
  54. sel_lang-0.3.0/tools/README.md +147 -0
@@ -0,0 +1,101 @@
1
+ # Build output. Everything in the repository is source; nothing generated is
2
+ # committed, so a fresh clone plus `tools/check.sh` is the whole story.
3
+
4
+ # --- C++
5
+ cpp/build/
6
+ *.o
7
+ *.a
8
+ *.so
9
+ *.dylib
10
+ *.dll
11
+ *.exe
12
+ # CMake, if you build that way instead of with the Makefile
13
+ cpp/cmake-build*/
14
+ CMakeCache.txt
15
+ CMakeFiles/
16
+ cmake_install.cmake
17
+ CTestTestfile.cmake
18
+ Testing/
19
+
20
+ # --- Common Lisp
21
+ # ASDF normally caches fasls under ~/.cache/common-lisp, but some setups keep
22
+ # them beside the source.
23
+ *.fasl
24
+ *.fas
25
+ *.lib
26
+ *.dx32fsl
27
+ *.dx64fsl
28
+ *.lx32fsl
29
+ *.lx64fsl
30
+ .cache/
31
+ # A saved core, if anyone builds one to speed the wrappers up
32
+ lisp/bin/*.core
33
+
34
+ # --- PHP and JS
35
+ # Neither implementation has runtime dependencies, but a consumer or a local
36
+ # `composer install` still produces these.
37
+ vendor/
38
+ node_modules/
39
+ composer.lock
40
+ package-lock.json
41
+ *.tgz
42
+
43
+ # --- Package manager build output
44
+ # Conan and vcpkg both build out of tree; these are their defaults.
45
+ cpp/test_package/build/
46
+ # Conan's CMakeToolchain writes this next to any conanfile it is run against,
47
+ # including the test package. Generated, not source.
48
+ CMakeUserPresets.json
49
+ conan.lock
50
+ CONAN_*
51
+ vcpkg_installed/
52
+
53
+ # --- Harness scratch
54
+ # tools/*.sh work in mktemp directories, so nothing should land here — these are
55
+ # for the times you run a tool by hand.
56
+ *.selc
57
+ oracle.txt
58
+ programs.json
59
+ /tmp-*
60
+
61
+ # --- Editors, tools and OS
62
+ .DS_Store
63
+ Thumbs.db
64
+ *.swp
65
+ *.swo
66
+ *~
67
+ \#*\#
68
+ .\#*
69
+ .idea/
70
+ .vscode/
71
+ *.sublime-workspace
72
+
73
+ # --- Rescue and patch debris
74
+ # An abandoned edit once left a zero-byte js/src/eval.mjs.orig behind, and
75
+ # `git add -A` committed it into a tagged release. npm happens to exclude *.orig
76
+ # by default, but Packagist, vcpkg and every GitHub source tarball ship the git
77
+ # tree verbatim, so it reached those. These patterns are the cheap insurance.
78
+ *.orig
79
+ *.bak
80
+ *.rej
81
+ *.tmp
82
+ *.old
83
+ *.save
84
+ *.patch
85
+ *.diff
86
+ *.log
87
+
88
+ # --- Generated JS bundle
89
+ # Built by `npm run build` and by prepublishOnly; never committed. A generated
90
+ # file in version control is a file that goes stale.
91
+ dist/
92
+
93
+ # Python
94
+ __pycache__/
95
+ *.py[cod]
96
+ *.egg-info/
97
+ python/.venv-wheel/
98
+ # `python -m build` writes here; dist/ itself already holds the JS bundle, so
99
+ # the Python artefacts are kept in their own subdirectory (see PACKAGING.md).
100
+ dist/python/
101
+ .pytest_cache/
sel_lang-0.3.0/LICENSE ADDED
@@ -0,0 +1,36 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Marcin Gałczyński
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
23
+ ---
24
+
25
+ Third-party components, which keep their own licences:
26
+
27
+ cpp/third_party/srell/ SRELL, Copyright (c) 2012-2026 Nozomu Katō
28
+ BSD 2-Clause. See cpp/third_party/srell/LICENSE.txt
29
+ and PINNED.md. Vendored, so it ships with the C++
30
+ implementation and its notice must travel too.
31
+
32
+ cl-ppcre BSD 2-Clause, Copyright (c) 2002-2015 Dr. Edmund Weitz.
33
+ A declared dependency of the Common Lisp system, not
34
+ vendored — installed by Quicklisp at build time.
35
+
36
+ The Python, PHP and JS implementations have no dependencies at all.
@@ -0,0 +1,288 @@
1
+ # Publishing
2
+
3
+ The package is called **`sel-lang`** on every registry. `sel` was already taken
4
+ in most of them: an npm CSS-selector library, a `projects/sel` entry in
5
+ quicklisp-projects (GrammaTech's Software Evolution Library), and a `sel` project
6
+ on PyPI.
7
+
8
+ | Registry | Name | Manifest |
9
+ |---|---|---|
10
+ | PyPI | `sel-lang` | `pyproject.toml` |
11
+ | npm | `sel-lang` | `package.json` |
12
+ | Packagist | `nathanjel/sel-lang` | `composer.json` |
13
+ | Quicklisp / Ultralisp | `sel-lang` | `lisp/sel-lang.asd` |
14
+ | Conan | `sel-lang` | `cpp/conanfile.py` |
15
+ | vcpkg | `sel-lang` | `cpp/vcpkg.json` |
16
+
17
+ Packagist requires a vendor prefix, so `nathanjel/` is unavoidable there. The
18
+ repository itself is `nathanjel/sel`; only the published package is `sel-lang`.
19
+
20
+ Nothing about publishing changes how the project is used without a package
21
+ manager: copying a directory still works, and that remains the primary story in
22
+ the README.
23
+
24
+ ---
25
+
26
+ ## Before any release
27
+
28
+ ```
29
+ tools/check.sh # ALL GREEN, full roster
30
+ SEL_IMPLS="$SEL_IMPLS python-wheel" tools/check.sh # and through the built wheel
31
+ tools/check-version.sh 0.3.0 # every manifest agrees
32
+ ```
33
+
34
+ The first must print `ALL GREEN` with every implementation present — a partial
35
+ roster is refused rather than quietly passing, because every differential layer
36
+ degrades to a no-op when there is nothing to compare against.
37
+
38
+ Then tag. Every registry below either reads the tag or is told the version by
39
+ hand, and they must agree:
40
+
41
+ ```
42
+ git tag -a v0.3.0 -m "SEL 0.3.0"
43
+ git push origin v0.3.0
44
+ ```
45
+
46
+ Versions live in six places. Keep them in step:
47
+
48
+ ```
49
+ package.json "version": "0.3.0"
50
+ pyproject.toml version = "0.3.0"
51
+ cpp/conanfile.py version = "0.3.0"
52
+ cpp/vcpkg.json "version-semver": "0.3.0"
53
+ cpp/CMakeLists.txt project(... VERSION 0.3.0 ...)
54
+ lisp/sel-lang.asd :version "0.3.0"
55
+ ```
56
+
57
+ `python/sel/__init__.py` carries `__version__` and is checked against
58
+ `pyproject.toml` by `tools/check-version.sh`, so it is one place fewer to
59
+ remember rather than one more.
60
+
61
+ `composer.json` deliberately carries **no** `version` field — Packagist infers it
62
+ from the git tag, and hard-coding it there is a known way to publish a lie.
63
+
64
+ ---
65
+
66
+ ## PyPI
67
+
68
+ ```
69
+ python3 -m build --outdir dist/python
70
+ python3 -m twine check dist/python/*
71
+ python3 -m twine upload dist/python/*
72
+ ```
73
+
74
+ **Build into `dist/python`, not `dist`.** The repository root's `dist/` already
75
+ holds the JavaScript bundle; letting `build` write beside it would mix two
76
+ languages' artefacts in one directory and eventually upload the wrong thing.
77
+
78
+ The wheel ships `python/sel/` and nothing else — the package, its `py.typed`
79
+ marker and the licence, about 50 kB. The sdist adds `docs/`, `spec/`,
80
+ `conformance/` and the Python examples, mirroring what npm's `files` whitelists.
81
+
82
+ The package has **no runtime dependencies**, and that is a property worth
83
+ keeping: the regex subset is small enough that `re` covers it after the anchor
84
+ rewrite, and the decimal core is deliberately hand-written (see below). Python
85
+ and JavaScript are the only two hosts with neither a vendored engine nor an
86
+ external one.
87
+
88
+ Verify the built package rather than the source tree, which is what the
89
+ `python-wheel` implementation in `tools/impls.sh` is for:
90
+
91
+ ```
92
+ python3 -m venv python/.venv-wheel
93
+ python/.venv-wheel/bin/pip install dist/python/*.whl
94
+ SEL_IMPLS="python-wheel" tools/check.sh
95
+ ```
96
+
97
+ That runs the whole conformance suite, the API probes and the fuzzer through the
98
+ *installed* package. It is the only layer that catches a packaging mistake — a
99
+ sub-package left out of the wheel, a missing `py.typed`, an entry point that
100
+ names a module the wheel does not contain — because every other layer imports
101
+ from `python/`.
102
+
103
+ ### Two things to know
104
+
105
+ **The `sel` console script collides with npm's.** Both packages install a command
106
+ called `sel`. There is no good way around it and no attempt is made to hide it:
107
+ `python -m sel` always works and is the spelling to prefer on a machine that has
108
+ both. It is the same trade as the `SEL` package name in Common Lisp — an unlikely
109
+ collision, made loud rather than silent.
110
+
111
+ **`python/sel/decimal.py` does not use the `decimal` module**, and must not start
112
+ to. `tools/decimal-oracle.py` generates this project's decimal test cases *from*
113
+ `decimal`, as an independent third opinion on cores that were all written from
114
+ one spec by one hand. A host built on `decimal` would turn `tools/check-decimal.sh`
115
+ into a comparison of the standard library with itself — still printing
116
+ "0 mismatches", while verifying nothing at all for that host.
117
+
118
+ For automated releases, PyPI's Trusted Publishing (OIDC from a CI workflow)
119
+ removes the need for a long-lived token; a manual `twine upload` with an API
120
+ token is equally fine and is what the commands above assume.
121
+
122
+ ---
123
+
124
+ ## npm
125
+
126
+ ```
127
+ npm pack --dry-run # inspect the file list first
128
+ npm publish --access public
129
+ ```
130
+
131
+ `files` in `package.json` whitelists what ships: `js/`, `docs/`, `spec/`, the JS
132
+ examples, the licence and the README. The PHP, C++ and Lisp trees are excluded,
133
+ so the tarball is ~73 kB rather than the whole repository.
134
+
135
+ The package is ESM-only (`"type": "module"`) and exposes one entry point plus the
136
+ `sel` CLI:
137
+
138
+ ```js
139
+ import { compile, evaluate, Value, SelError } from 'sel-lang';
140
+ ```
141
+
142
+ `sideEffects` lists `js/src/builtins/*.mjs`, because those modules register
143
+ themselves in the function table and a bundler that tree-shook them would leave
144
+ you with a language that has no functions in it.
145
+
146
+ ---
147
+
148
+ ## Packagist
149
+
150
+ Submit the GitHub URL once at <https://packagist.org/packages/submit>, then add
151
+ the GitHub webhook so subsequent tags publish themselves.
152
+
153
+ Autoloading is a single `files` entry pointing at `php/src/bootstrap.php`, not
154
+ PSR-4. That is deliberate: the function table must be complete before any source
155
+ is parsed, because an unknown function name is a *compile-time* error, and PSR-4
156
+ would only load a class at the moment it is first mentioned. `bootstrap.php` uses
157
+ `require_once` throughout, so loading it twice is harmless.
158
+
159
+ Verify before publishing:
160
+
161
+ ```
162
+ composer validate
163
+ ```
164
+
165
+ ---
166
+
167
+ ## Quicklisp and Ultralisp
168
+
169
+ The ASDF system is `sel-lang`, defined in `lisp/sel-lang.asd`. ASDF requires the
170
+ file name to match the primary system name, which is why the file was renamed.
171
+
172
+ **Ultralisp** is the quicker of the two: add the repository at
173
+ <https://ultralisp.org/>, and it scans for `.asd` files itself — including in
174
+ subdirectories, so `lisp/sel-lang.asd` is found without moving anything.
175
+
176
+ **Quicklisp** needs a pull request against
177
+ <https://github.com/quicklisp/quicklisp-projects> adding `projects/sel-lang/source.txt`:
178
+
179
+ ```
180
+ git https://github.com/nathanjel/sel.git
181
+ ```
182
+
183
+ Releases are cut monthly, so expect a wait.
184
+
185
+ ### One thing to know
186
+
187
+ The ASDF system is `sel-lang` but the Common Lisp *package* is still `SEL`, so
188
+ the API reads `sel:evaluate`. If you ever load this alongside GrammaTech's
189
+ Software Evolution Library in one image, the package names may collide. Renaming
190
+ the package would change every call site in the public API; it has not been done
191
+ because the collision is unlikely and loud rather than silent.
192
+
193
+ ---
194
+
195
+ ## A note for C++ consumers upgrading to 0.3.0
196
+
197
+ `sel::Value` became a handle: copying one now aliases, and `clone()` is the deep
198
+ copy. Every signature in `sel.hpp` is unchanged, so this compiles silently — the
199
+ break is behavioural, not a build error, which is the awkward kind.
200
+
201
+ ```cpp
202
+ Value b = a; // 0.2.0: an independent deep copy
203
+ // 0.3.0: the same value as a
204
+ Value b = a.clone(); // an independent deep copy, both versions
205
+ ```
206
+
207
+ The interpreter needed this to agree with the other four hosts (spec/SPEC.md
208
+ §3.4), and it makes copies cheap. Code that builds each value fresh and moves it
209
+ into place — the idiom `cpp/bin/e2e.cpp` already uses — needs no change at all.
210
+
211
+ ## Conan
212
+
213
+ Conan Center does **not** have SRELL, so the vendored copy is what makes the
214
+ recipe build at all. See the note below.
215
+
216
+ Conan needs a profile before its first use, or it refuses with *"The default
217
+ build profile doesn't exist"* — that is Conan asking to be initialised, not a
218
+ problem with the recipe:
219
+
220
+ ```
221
+ conan profile detect # once, per machine
222
+ conan create cpp/ --build=missing
223
+ ```
224
+
225
+ `conan create` also builds and runs `cpp/test_package/`, which links the
226
+ packaged library through the exported CMake target and nothing else, so a
227
+ recipe that produces an unusable package fails there rather than downstream.
228
+
229
+ The recipe does **not** gate on the consumer's `compiler.cppstd`. A stock
230
+ `conan profile detect` yields `gnu20`, and the library reaches C++23 through
231
+ `target_compile_features` in CMakeLists.txt, so refusing to build on the default
232
+ profile would only make the package unusable out of the box.
233
+
234
+ To publish, either upload to your own remote:
235
+
236
+ ```
237
+ conan upload sel-lang/0.3.0 -r <remote> --confirm
238
+ ```
239
+
240
+ or open a pull request against
241
+ <https://github.com/conan-io/conan-center-index> adding `recipes/sel-lang/`.
242
+ Conan Center requires the recipe to fetch sources from a release URL rather than
243
+ carry them, so a Center submission needs the recipe reworked around
244
+ `conan.tools.files.get()` pointing at the GitHub tarball for the tag.
245
+
246
+ ---
247
+
248
+ ## vcpkg
249
+
250
+ `cpp/vcpkg.json` is a manifest, usable immediately in overlay-port form. For the
251
+ public registry, open a pull request against
252
+ <https://github.com/microsoft/vcpkg> adding `ports/sel-lang/` with a
253
+ `portfile.cmake` that calls `vcpkg_from_github`, `vcpkg_cmake_configure`,
254
+ `vcpkg_cmake_install` and `vcpkg_cmake_config_fixup(PACKAGE_NAME sel-lang)`.
255
+
256
+ ---
257
+
258
+ ## SRELL: vendored, with an opt-out
259
+
260
+ The C++ implementation needs an ECMAScript-conformant regex engine, because that
261
+ is what makes it agree with the JavaScript host. It vendors SRELL, pinned to
262
+ release **2026.05** at commit `7bf06e58…`, under `cpp/third_party/srell/`.
263
+
264
+ Where each package manager stands:
265
+
266
+ | | SRELL available? | what SEL does |
267
+ |---|---|---|
268
+ | vcpkg | **yes**, `srell` at exactly `2026.05` | vendored by default; `system-srell` feature links vcpkg's |
269
+ | Conan | no such package | vendored, no alternative |
270
+ | plain CMake / copy-two-files | n/a | vendored |
271
+
272
+ The vendored copy is the default everywhere, on purpose. It is what keeps "copy
273
+ `sel.hpp`, `sel.cpp` and `third_party/srell/` and compile" true, it is the only
274
+ option for Conan, and it removes any chance of a resolver quietly selecting a
275
+ different engine version — which would not be a build difference, it would be a
276
+ *language* difference, since the regex engine decides what a rule matches.
277
+
278
+ To link an external SRELL instead:
279
+
280
+ ```
281
+ cmake -S cpp -B build -DSEL_USE_SYSTEM_SRELL=ON # needs find_package(srell)
282
+ vcpkg install sel-lang[system-srell]
283
+ ```
284
+
285
+ `cpp/vcpkg.json` pins `srell` to `2026.05` in `overrides` so the feature cannot
286
+ silently drift to another release. Either way, **run `tools/check.sh`**: the
287
+ regex cases in `conformance/09-regex.selt` are what actually decide whether a
288
+ given SRELL still agrees with the other three implementations.