gramson 0.1.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.
- gramson-0.1.0/LICENSE +21 -0
- gramson-0.1.0/MANIFEST.in +9 -0
- gramson-0.1.0/Makefile +65 -0
- gramson-0.1.0/PKG-INFO +180 -0
- gramson-0.1.0/README.md +111 -0
- gramson-0.1.0/THIRD_PARTY_NOTICES.md +15 -0
- gramson-0.1.0/docs/DESIGN.md +55 -0
- gramson-0.1.0/docs/DEVELOPMENT.md +111 -0
- gramson-0.1.0/docs/PYPI.md +149 -0
- gramson-0.1.0/docs/README.md +90 -0
- gramson-0.1.0/docs/USAGE.md +144 -0
- gramson-0.1.0/docs/index.html +25 -0
- gramson-0.1.0/docs/json-value.schema.yaml +22 -0
- gramson-0.1.0/docs/openapi.yaml +74 -0
- gramson-0.1.0/examples/bitwise/README.md +115 -0
- gramson-0.1.0/examples/bitwise/expressions/conversions.json +10 -0
- gramson-0.1.0/examples/bitwise/expressions/conversions.yaml +5 -0
- gramson-0.1.0/examples/bitwise/expressions/invalid-arity.yaml +1 -0
- gramson-0.1.0/examples/bitwise/expressions/invalid-conversion-arity.yaml +1 -0
- gramson-0.1.0/examples/bitwise/expressions/nested.json +7 -0
- gramson-0.1.0/examples/bitwise/expressions/nested.yaml +13 -0
- gramson-0.1.0/examples/bitwise/language.yaml +3 -0
- gramson-0.1.0/examples/extension/README.md +62 -0
- gramson-0.1.0/examples/extension/custom/wrapping.yaml +9 -0
- gramson-0.1.0/examples/extension/expressions/invalid-arity.yaml +1 -0
- gramson-0.1.0/examples/extension/expressions/nested.json +6 -0
- gramson-0.1.0/examples/extension/expressions/nested.yaml +5 -0
- gramson-0.1.0/examples/extension/expressions/wrap-only.yaml +3 -0
- gramson-0.1.0/examples/extension/language.yaml +4 -0
- gramson-0.1.0/examples/extension/standalone.yaml +2 -0
- gramson-0.1.0/examples/external/README.md +73 -0
- gramson-0.1.0/examples/external/expressions/invalid-arity.yaml +1 -0
- gramson-0.1.0/examples/external/expressions/nested.json +3 -0
- gramson-0.1.0/examples/external/expressions/nested.yaml +6 -0
- gramson-0.1.0/examples/external/language.yaml +3 -0
- gramson-0.1.0/examples/external/vendor/mystuff/myops/wrapping.yaml +7 -0
- gramson-0.1.0/examples/invalid.json +349 -0
- gramson-0.1.0/examples/valid.json +851 -0
- gramson-0.1.0/gramson/__init__.py +1 -0
- gramson-0.1.0/gramson/__main__.py +3 -0
- gramson-0.1.0/gramson/cli.py +195 -0
- gramson-0.1.0/gramson/generation.py +366 -0
- gramson-0.1.0/gramson.egg-info/PKG-INFO +180 -0
- gramson-0.1.0/gramson.egg-info/SOURCES.txt +87 -0
- gramson-0.1.0/gramson.egg-info/dependency_links.txt +1 -0
- gramson-0.1.0/gramson.egg-info/entry_points.txt +2 -0
- gramson-0.1.0/gramson.egg-info/requires.txt +3 -0
- gramson-0.1.0/gramson.egg-info/top_level.txt +1 -0
- gramson-0.1.0/pyproject.toml +60 -0
- gramson-0.1.0/requirements-dev.txt +4 -0
- gramson-0.1.0/requirements-release.txt +3 -0
- gramson-0.1.0/schema/__init__.py +1 -0
- gramson-0.1.0/schema/core/arithmetic.schema.yaml +121 -0
- gramson-0.1.0/schema/core/collection.schema.yaml +146 -0
- gramson-0.1.0/schema/core/comparison.schema.yaml +131 -0
- gramson-0.1.0/schema/core/data.schema.yaml +65 -0
- gramson-0.1.0/schema/core/debugging.schema.yaml +24 -0
- gramson-0.1.0/schema/core/logic.schema.yaml +129 -0
- gramson-0.1.0/schema/core/string.schema.yaml +38 -0
- gramson-0.1.0/schema/expression.schema.yaml +48 -0
- gramson-0.1.0/schema/shared.schema.yaml +9 -0
- gramson-0.1.0/setup.cfg +4 -0
- gramson-0.1.0/spec/README.md +302 -0
- gramson-0.1.0/spec/__init__.py +1 -0
- gramson-0.1.0/spec/core/arithmetic.yaml +52 -0
- gramson-0.1.0/spec/core/collection.yaml +70 -0
- gramson-0.1.0/spec/core/comparison.yaml +64 -0
- gramson-0.1.0/spec/core/data.yaml +29 -0
- gramson-0.1.0/spec/core/debugging.yaml +10 -0
- gramson-0.1.0/spec/core/logic.yaml +58 -0
- gramson-0.1.0/spec/core/string.yaml +16 -0
- gramson-0.1.0/spec/extensions/bitwise.yaml +67 -0
- gramson-0.1.0/spec/language.yaml +2 -0
- gramson-0.1.0/tests/jsonlogic_exclusions.json +1 -0
- gramson-0.1.0/tests/schema_support.py +21 -0
- gramson-0.1.0/tests/test_bitwise.py +82 -0
- gramson-0.1.0/tests/test_cli.py +292 -0
- gramson-0.1.0/tests/test_external_includes.py +164 -0
- gramson-0.1.0/tests/test_generation.py +249 -0
- gramson-0.1.0/tests/test_jsonlogic.py +72 -0
- gramson-0.1.0/tests/test_language.py +299 -0
- gramson-0.1.0/tests/test_openapi.py +130 -0
- gramson-0.1.0/tests/test_validation.py +47 -0
- gramson-0.1.0/tests/vendor/jsonlogic/LICENSE +22 -0
- gramson-0.1.0/tests/vendor/jsonlogic/README.md +48 -0
- gramson-0.1.0/tests/vendor/jsonlogic/snapshot.json +20 -0
- gramson-0.1.0/tests/vendor/jsonlogic/tests.json +532 -0
- gramson-0.1.0/tools/check_distribution.py +172 -0
- gramson-0.1.0/tools/generate_schema.py +33 -0
gramson-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Kim Bäckström
|
|
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.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
include README.md Makefile requirements-dev.txt requirements-release.txt
|
|
2
|
+
recursive-include spec *.yaml *.yml *.md
|
|
3
|
+
recursive-include schema *.schema.yaml
|
|
4
|
+
recursive-include tools *.py
|
|
5
|
+
recursive-include tests *.py *.json *.md LICENSE
|
|
6
|
+
recursive-include examples *.json *.yaml *.yml *.md
|
|
7
|
+
recursive-include docs *.md *.yaml *.html
|
|
8
|
+
exclude docs/RELEASING.md
|
|
9
|
+
global-exclude __pycache__ *.py[cod]
|
gramson-0.1.0/Makefile
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
.DEFAULT_GOAL := help
|
|
2
|
+
|
|
3
|
+
PYTHON ?= python3
|
|
4
|
+
VENV ?= .venv
|
|
5
|
+
VENV_PYTHON := $(VENV)/bin/python
|
|
6
|
+
HOST ?= 127.0.0.1
|
|
7
|
+
PORT ?= 8765
|
|
8
|
+
|
|
9
|
+
.PHONY: help setup setup-release generate example example-bitwise example-external lint lint-python lint-openapi test check build check-dist serve
|
|
10
|
+
|
|
11
|
+
help: ## Show available targets
|
|
12
|
+
@awk 'BEGIN {FS = ":.*## "} /^[a-z-]+:.*## / {printf " %-16s %s\n", $$1, $$2}' $(MAKEFILE_LIST)
|
|
13
|
+
|
|
14
|
+
$(VENV_PYTHON):
|
|
15
|
+
$(PYTHON) -m venv "$(VENV)"
|
|
16
|
+
|
|
17
|
+
setup: $(VENV_PYTHON) ## Create the virtual environment and install the CLI and development dependencies
|
|
18
|
+
"$(VENV_PYTHON)" -m pip install -r requirements-dev.txt
|
|
19
|
+
"$(VENV_PYTHON)" -m pip install -e .
|
|
20
|
+
|
|
21
|
+
setup-release: setup ## Install development and release tooling
|
|
22
|
+
"$(VENV_PYTHON)" -m pip install -r requirements-release.txt
|
|
23
|
+
|
|
24
|
+
generate: ## Generate JSON Schemas from the operator specification
|
|
25
|
+
"$(VENV_PYTHON)" tools/generate_schema.py
|
|
26
|
+
|
|
27
|
+
example: ## Generate and validate the ready-to-run core extension example
|
|
28
|
+
"$(VENV_PYTHON)" -m gramson generate --manifest examples/extension/language.yaml --output-directory build/examples/extension
|
|
29
|
+
"$(VENV_PYTHON)" -m gramson validate examples/extension/expressions/nested.yaml --schema build/examples/extension/expression.schema.yaml
|
|
30
|
+
"$(VENV_PYTHON)" -m gramson validate examples/extension/expressions/nested.json --schema build/examples/extension/expression.schema.yaml
|
|
31
|
+
|
|
32
|
+
example-bitwise: ## Generate and validate the bitwise extension example
|
|
33
|
+
"$(VENV_PYTHON)" -m gramson generate --manifest examples/bitwise/language.yaml --output-directory build/examples/bitwise
|
|
34
|
+
"$(VENV_PYTHON)" -m gramson validate examples/bitwise/expressions/nested.yaml --schema build/examples/bitwise/expression.schema.yaml
|
|
35
|
+
"$(VENV_PYTHON)" -m gramson validate examples/bitwise/expressions/nested.json --schema build/examples/bitwise/expression.schema.yaml
|
|
36
|
+
"$(VENV_PYTHON)" -m gramson validate examples/bitwise/expressions/conversions.yaml --schema build/examples/bitwise/expression.schema.yaml
|
|
37
|
+
"$(VENV_PYTHON)" -m gramson validate examples/bitwise/expressions/conversions.json --schema build/examples/bitwise/expression.schema.yaml
|
|
38
|
+
|
|
39
|
+
example-external: ## Generate and validate an external namespace include
|
|
40
|
+
"$(VENV_PYTHON)" -m gramson generate --manifest examples/external/language.yaml --spec-path examples/external/vendor --output-directory build/examples/external
|
|
41
|
+
"$(VENV_PYTHON)" -m gramson validate examples/external/expressions/nested.yaml --schema build/examples/external/expression.schema.yaml
|
|
42
|
+
"$(VENV_PYTHON)" -m gramson validate examples/external/expressions/nested.json --schema build/examples/external/expression.schema.yaml
|
|
43
|
+
|
|
44
|
+
lint: lint-python lint-openapi ## Run Python and OpenAPI linting
|
|
45
|
+
|
|
46
|
+
lint-python: ## Lint Python code and import ordering with Ruff
|
|
47
|
+
"$(VENV_PYTHON)" -m ruff check .
|
|
48
|
+
|
|
49
|
+
lint-openapi: ## Validate the OpenAPI document and its external references
|
|
50
|
+
"$(VENV_PYTHON)" -m openapi_spec_validator docs/openapi.yaml
|
|
51
|
+
|
|
52
|
+
test: ## Run all tests, including generated-schema freshness checks
|
|
53
|
+
"$(VENV_PYTHON)" -m unittest discover -s tests -v
|
|
54
|
+
|
|
55
|
+
check: lint test ## Run lint and tests without regenerating files
|
|
56
|
+
|
|
57
|
+
build: ## Build a source distribution and a wheel from that source distribution
|
|
58
|
+
"$(VENV_PYTHON)" -m build
|
|
59
|
+
|
|
60
|
+
check-dist: build ## Check package metadata, bundled schemas, and installed CLI
|
|
61
|
+
"$(VENV_PYTHON)" tools/check_distribution.py dist
|
|
62
|
+
|
|
63
|
+
serve: generate ## Generate schemas and serve the Swagger viewer locally
|
|
64
|
+
@printf 'Swagger viewer: http://%s:%s/docs/\n' "$(HOST)" "$(PORT)"
|
|
65
|
+
"$(VENV_PYTHON)" -m http.server "$(PORT)" --bind "$(HOST)"
|
gramson-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: gramson
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Define JSON expression languages declaratively
|
|
5
|
+
Author: Kim Bäckström
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/kbck/gramson
|
|
8
|
+
Project-URL: Documentation, https://github.com/kbck/gramson#readme
|
|
9
|
+
Project-URL: Repository, https://github.com/kbck/gramson
|
|
10
|
+
Project-URL: Issues, https://github.com/kbck/gramson/issues
|
|
11
|
+
Keywords: json-logic,json-schema,expressions,validation,yaml
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Environment :: Console
|
|
14
|
+
Classifier: Intended Audience :: Developers
|
|
15
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Topic :: Software Development :: Code Generators
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
License-File: THIRD_PARTY_NOTICES.md
|
|
26
|
+
License-File: tests/vendor/jsonlogic/LICENSE
|
|
27
|
+
Requires-Dist: jsonschema<5,>=4.18
|
|
28
|
+
Requires-Dist: PyYAML<7,>=6
|
|
29
|
+
Requires-Dist: referencing<1,>=0.28
|
|
30
|
+
Dynamic: license-file
|
|
31
|
+
|
|
32
|
+
# Gramson
|
|
33
|
+
|
|
34
|
+
**Define JSON expression languages declaratively.**
|
|
35
|
+
|
|
36
|
+
Gramson compiles composable YAML operator definitions into recursive JSON Schema
|
|
37
|
+
grammars. Its CLI generates languages and validates JSON or YAML expressions,
|
|
38
|
+
with readable error locations and no network requests during validation.
|
|
39
|
+
|
|
40
|
+
## Install and validate
|
|
41
|
+
|
|
42
|
+
Requires Python 3.10 or newer. Install in a virtual environment:
|
|
43
|
+
|
|
44
|
+
```sh
|
|
45
|
+
python -m pip install gramson
|
|
46
|
+
gramson validate expression.json
|
|
47
|
+
gramson validate expression.yaml
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
For example, `expression.yaml` can contain:
|
|
51
|
+
|
|
52
|
+
```yaml
|
|
53
|
+
if:
|
|
54
|
+
- '>':
|
|
55
|
+
- var: age
|
|
56
|
+
- 18
|
|
57
|
+
- adult
|
|
58
|
+
- minor
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Read one expression from stdin:
|
|
62
|
+
|
|
63
|
+
```sh
|
|
64
|
+
printf '%s\n' '{"var":"price"}' | gramson validate -
|
|
65
|
+
printf '%s\n' 'var: price' | gramson validate --format yaml -
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Files ending in `.yaml` or `.yml` are read as YAML; other files and stdin default
|
|
69
|
+
to JSON. Use `--format json` or `--format yaml` to override detection. Each input
|
|
70
|
+
contains one expression, without an API request envelope. YAML values must be
|
|
71
|
+
representable as JSON: duplicate or non-string keys, non-finite numbers,
|
|
72
|
+
timestamps, and cyclic aliases are rejected. Quote dates to use them as strings.
|
|
73
|
+
|
|
74
|
+
Exit codes are `0` for valid syntax, `1` for an invalid expression, and `2` for
|
|
75
|
+
input, schema, or command usage errors. `python -m gramson validate ...` is also supported.
|
|
76
|
+
|
|
77
|
+
## Generate and extend a language
|
|
78
|
+
|
|
79
|
+
The [ready-to-run extension example](https://github.com/kbck/gramson/tree/main/examples/extension)
|
|
80
|
+
contains manifests, a custom operator, JSON/YAML expressions, and instructions.
|
|
81
|
+
It is also included in the source distribution; run `make setup` and `make example`
|
|
82
|
+
from its repository or source-distribution root.
|
|
83
|
+
The [bitwise extension](https://github.com/kbck/gramson/tree/main/examples/bitwise)
|
|
84
|
+
adds `&`, `|`, `^`, `~`, `<<`, `>>`, `hexToInt`, and `intToHex`;
|
|
85
|
+
run it with `make example-bitwise`.
|
|
86
|
+
Its reusable declarations are bundled in Gramson and can be selected directly:
|
|
87
|
+
|
|
88
|
+
```yaml
|
|
89
|
+
include:
|
|
90
|
+
- gramson:core
|
|
91
|
+
- gramson:bitwise
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
No declaration copying is needed. Add `operatorDirectories` for local groups,
|
|
95
|
+
or include only `gramson:bitwise` to select it without core. Duplicate includes,
|
|
96
|
+
unknown names, and operator, definition-name, or output-path collisions are errors.
|
|
97
|
+
|
|
98
|
+
User-owned libraries can use their own namespaces:
|
|
99
|
+
|
|
100
|
+
```yaml
|
|
101
|
+
include:
|
|
102
|
+
- gramson:core
|
|
103
|
+
- mystuff:myops
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
```sh
|
|
107
|
+
gramson generate --manifest language.yaml --spec-path ./vendor --output-directory generated
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
This reads operator declarations from `vendor/mystuff/myops/` recursively.
|
|
111
|
+
Repeat `--spec-path` for multiple libraries; paths are relative to the command's
|
|
112
|
+
working directory. `gramson:` is reserved for bundled definitions. Unknown or
|
|
113
|
+
ambiguous includes fail, and namespace labels do not prefix operator keys.
|
|
114
|
+
Generated schemas remain self-contained and validate without the source library.
|
|
115
|
+
See the [external library example](https://github.com/kbck/gramson/tree/main/examples/external),
|
|
116
|
+
also runnable with `make example-external` from the source distribution.
|
|
117
|
+
|
|
118
|
+
`gramson generate --output-directory generated` writes the bundled core grammar
|
|
119
|
+
as a self-contained directory. For a custom language, create a manifest:
|
|
120
|
+
|
|
121
|
+
```yaml
|
|
122
|
+
# my-language/language.yaml
|
|
123
|
+
include:
|
|
124
|
+
- gramson:core
|
|
125
|
+
operatorDirectories:
|
|
126
|
+
- custom
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
Add declarations in `my-language/custom/wrapping.yaml`:
|
|
130
|
+
|
|
131
|
+
```yaml
|
|
132
|
+
wrap:
|
|
133
|
+
schemaName: wrapper
|
|
134
|
+
title: Wrapper
|
|
135
|
+
summary: Wrap one expression.
|
|
136
|
+
argumentKind: expression
|
|
137
|
+
minArgs: 1
|
|
138
|
+
maxArgs: 1
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
```sh
|
|
142
|
+
gramson generate --manifest my-language/language.yaml --output-directory generated
|
|
143
|
+
gramson validate expression.yaml --schema generated/expression.schema.yaml
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Generated output includes core and custom schemas, shared definitions, and one
|
|
147
|
+
expression entry point; operators can nest across groups. Keep the output
|
|
148
|
+
directory together when moving it. Validation against `--schema` uses those
|
|
149
|
+
files offline and does not depend on the installed core language.
|
|
150
|
+
|
|
151
|
+
Without `include`, a manifest selects only local directories.
|
|
152
|
+
`include: [gramson:core]` alone selects just core. Include order does not give
|
|
153
|
+
precedence: duplicate operator keys, definition names, or output paths fail the
|
|
154
|
+
whole generation. Pin the Gramson version for reproducible bundled declarations.
|
|
155
|
+
Custom declarations define syntax; they do not implement evaluation.
|
|
156
|
+
|
|
157
|
+
## Compatibility and contents
|
|
158
|
+
|
|
159
|
+
The included JSON Logic syntax profile covers all 34 documented operators plus
|
|
160
|
+
the `?:` alias. All 278 expressions in the pinned upstream corpus pass syntax
|
|
161
|
+
validation. Gramson applies stricter argument counts and object-shape rules than
|
|
162
|
+
the reference interpreter and validates every branch. There is no evaluator;
|
|
163
|
+
runtime equivalence is not tested.
|
|
164
|
+
|
|
165
|
+
The wheel includes the generated schemas under `gramson.schemas`, preserving
|
|
166
|
+
relative references for offline use. They can be accessed with
|
|
167
|
+
`importlib.resources.files("gramson.schemas")`. Core and optional extension declarations are bundled
|
|
168
|
+
under `gramson.specs`, and the generator is included in the installed package.
|
|
169
|
+
The source distribution also contains tests and user documentation.
|
|
170
|
+
|
|
171
|
+
See the [repository and development guide](https://github.com/kbck/gramson#readme),
|
|
172
|
+
[operator specification](https://github.com/kbck/gramson/blob/main/spec/README.md),
|
|
173
|
+
and [issue tracker](https://github.com/kbck/gramson/issues).
|
|
174
|
+
|
|
175
|
+
## License and acknowledgements
|
|
176
|
+
|
|
177
|
+
Gramson is licensed under MIT. Its included expression language builds on
|
|
178
|
+
[JSON Logic](https://jsonlogic.com/), created by Jeremy Wadhams. The vendored
|
|
179
|
+
upstream tests retain their original MIT license and attribution; see the
|
|
180
|
+
[third-party notices](https://github.com/kbck/gramson/blob/main/THIRD_PARTY_NOTICES.md).
|
gramson-0.1.0/README.md
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Gramson
|
|
2
|
+
|
|
3
|
+
**Define operators once. Generate the grammar that validates your JSON expressions.**
|
|
4
|
+
|
|
5
|
+
Adding operators to a JSON expression language means keeping its syntax rules
|
|
6
|
+
and validation in sync. Gramson compiles YAML operator declarations into a
|
|
7
|
+
recursive JSON Schema grammar, so adding an operator or changing its arguments
|
|
8
|
+
doesn't require hand-editing the validation schema.
|
|
9
|
+
|
|
10
|
+
## Why Gramson?
|
|
11
|
+
|
|
12
|
+
JSON Schema can already validate recursive expressions. Gramson generates the
|
|
13
|
+
operator objects, argument-count constraints, shorthand forms, and references
|
|
14
|
+
from your declarations. JSON Logic supplies the bundled starting profile;
|
|
15
|
+
you can extend it or define an independent language.
|
|
16
|
+
|
|
17
|
+
**Operator libraries become composable grammar modules.** Include groups from
|
|
18
|
+
different libraries, and their operators can nest inside one another. The
|
|
19
|
+
combined grammar is generated from the selected definitions and works with
|
|
20
|
+
standard Draft 2020-12 JSON Schema validators.
|
|
21
|
+
|
|
22
|
+
Gramson is a Python library and CLI. It defines and validates syntax; it does
|
|
23
|
+
not evaluate expressions.
|
|
24
|
+
|
|
25
|
+
## Example
|
|
26
|
+
|
|
27
|
+
Declare an operator that takes exactly one expression:
|
|
28
|
+
|
|
29
|
+
```yaml
|
|
30
|
+
wrap:
|
|
31
|
+
argumentKind: expression
|
|
32
|
+
minArgs: 1
|
|
33
|
+
maxArgs: 1
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Combine it with the bundled core language, and the generated schema accepts:
|
|
37
|
+
|
|
38
|
+
```json
|
|
39
|
+
{"wrap": [{"+": [1, 2]}]}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
It rejects `{"wrap": [1, 2]}` because `wrap` takes one argument. Recursive
|
|
43
|
+
references let your operators nest inside core operators, and vice versa.
|
|
44
|
+
|
|
45
|
+
## Try it
|
|
46
|
+
|
|
47
|
+
Requires Python 3.10 or newer. From a checkout:
|
|
48
|
+
|
|
49
|
+
```sh
|
|
50
|
+
make setup
|
|
51
|
+
make example
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
This installs the CLI, generates a language with `wrap`, and validates the
|
|
55
|
+
included JSON and YAML expressions. The [complete example](examples/extension/README.md)
|
|
56
|
+
contains the manifest, declarations, and expressions ready to edit.
|
|
57
|
+
|
|
58
|
+
For your own files, the workflow is:
|
|
59
|
+
|
|
60
|
+
```sh
|
|
61
|
+
. .venv/bin/activate
|
|
62
|
+
gramson generate --manifest language.yaml --output-directory generated
|
|
63
|
+
gramson validate expression.yaml --schema generated/expression.schema.yaml
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
Generated output is self-contained and validates offline. Without `--schema`,
|
|
67
|
+
validation uses the bundled core grammar. See the [usage guide](docs/USAGE.md)
|
|
68
|
+
for input formats, stdin, exit codes, and custom languages.
|
|
69
|
+
|
|
70
|
+
## Compose a language
|
|
71
|
+
|
|
72
|
+
Select bundled groups and local declarations in a manifest:
|
|
73
|
+
|
|
74
|
+
```yaml
|
|
75
|
+
include:
|
|
76
|
+
- gramson:core
|
|
77
|
+
- gramson:bitwise
|
|
78
|
+
operatorDirectories:
|
|
79
|
+
- custom
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
- **Core:** a JSON Logic syntax profile covering 34 documented operators plus
|
|
83
|
+
`?:`. All 278 expressions in the pinned upstream corpus pass syntax validation.
|
|
84
|
+
Structural rules are stricter than the reference interpreter; runtime
|
|
85
|
+
equivalence is untested.
|
|
86
|
+
- **Bitwise:** six bitwise operators plus `hexToInt` and `intToHex` conversion
|
|
87
|
+
forms. Try `make example-bitwise`.
|
|
88
|
+
- **Your libraries:** include names such as `mystuff:myops` with explicit
|
|
89
|
+
`--spec-path` search roots. Try `make example-external`.
|
|
90
|
+
|
|
91
|
+
Include order gives no precedence: duplicate operator keys, definition names,
|
|
92
|
+
or output paths fail generation. The [specification guide](spec/README.md)
|
|
93
|
+
explains declarations and include resolution.
|
|
94
|
+
|
|
95
|
+
## Documentation
|
|
96
|
+
|
|
97
|
+
- [Usage and CLI](docs/USAGE.md)
|
|
98
|
+
- [Operator specification and manifest format](spec/README.md)
|
|
99
|
+
- [Scope, compatibility boundaries, and open questions](docs/DESIGN.md)
|
|
100
|
+
- [Development commands and repository layout](docs/DEVELOPMENT.md)
|
|
101
|
+
- [Swagger schema viewer](docs/README.md)
|
|
102
|
+
|
|
103
|
+
To work on Gramson, run `make check` for Ruff, OpenAPI validation, and tests.
|
|
104
|
+
Run `make help` for all targets.
|
|
105
|
+
|
|
106
|
+
## License and acknowledgements
|
|
107
|
+
|
|
108
|
+
[MIT](LICENSE). The included JSON Logic profile builds on
|
|
109
|
+
[JSON Logic](https://jsonlogic.com/), created by Jeremy Wadhams. Its shared test
|
|
110
|
+
corpus is vendored with the original MIT license and attribution; see
|
|
111
|
+
[third-party notices](THIRD_PARTY_NOTICES.md).
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Third-party notices
|
|
2
|
+
|
|
3
|
+
## JSON Logic shared tests
|
|
4
|
+
|
|
5
|
+
The files `tests/vendor/jsonlogic/tests.json` and
|
|
6
|
+
`tests/vendor/jsonlogic/LICENSE` are copied verbatim from the upstream JSON Logic
|
|
7
|
+
project maintained by Jeremy Wadhams.
|
|
8
|
+
|
|
9
|
+
Copyright (c) 2015 Jeremy Wadhams. Licensed under the MIT License; the complete
|
|
10
|
+
copyright, permission, and warranty notice is included in
|
|
11
|
+
[tests/vendor/jsonlogic/LICENSE](tests/vendor/jsonlogic/LICENSE).
|
|
12
|
+
|
|
13
|
+
The [snapshot metadata](tests/vendor/jsonlogic/snapshot.json) identifies the
|
|
14
|
+
immutable upstream revisions, source URLs, and SHA-256 checksums. See the
|
|
15
|
+
[vendoring guide](tests/vendor/jsonlogic/README.md) for usage and update rules.
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Scope and design decisions
|
|
2
|
+
|
|
3
|
+
## Scope and decisions
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
- Accept JSON scalar literals and recursive arrays of expressions.
|
|
7
|
+
- Represent each operation as a closed object with exactly one registered key.
|
|
8
|
+
- Support data access, conditionals, Boolean logic, comparisons, arithmetic,
|
|
9
|
+
collections, strings, and logging syntax.
|
|
10
|
+
- Accept variable defaults, numeric and computed keys, unary shorthand, and
|
|
11
|
+
the documented arity profile.
|
|
12
|
+
- Reject unknown operators and arbitrary object literals throughout expressions,
|
|
13
|
+
including inside arrays, defaults, and unselected branches.
|
|
14
|
+
|
|
15
|
+
This specifies structural acceptance. It does not implement evaluation or
|
|
16
|
+
semantic typing. For example, `{"+": [true, "2", null]}` and
|
|
17
|
+
`{"map": [true, "body"]}` pass syntax validation without promising meaningful
|
|
18
|
+
evaluation. The profile constrains arities rather than accepting every malformed
|
|
19
|
+
call that a permissive interpreter might process.
|
|
20
|
+
|
|
21
|
+
`if` and its `?:` alias accept condition/result pairs with an optional final fallback. The agreed
|
|
22
|
+
runtime result is `null` when no condition matches and no fallback is supplied;
|
|
23
|
+
there is no evaluator yet to execute that rule.
|
|
24
|
+
Empty argument lists are accepted and yield `null` in JSON Logic. A single
|
|
25
|
+
argument is a fallback expression; non-array shorthand is also accepted.
|
|
26
|
+
|
|
27
|
+
Results are data rather than expressions. The hand-written
|
|
28
|
+
[JSON-value schema](json-value.schema.yaml) accepts scalars, recursive
|
|
29
|
+
arrays, and arbitrary objects. It reuses `scalarLiteral` from the generated shared schema
|
|
30
|
+
but defines its own recursive containers so result objects are never treated as
|
|
31
|
+
operator calls.
|
|
32
|
+
|
|
33
|
+
## Open design questions
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
- **Semantic typing:** Should a separate phase check operand and result types?
|
|
37
|
+
How would it learn variable types and handle null or missing values?
|
|
38
|
+
- **Explicit conversions:** This remains the preferred direction. Unary `+` and
|
|
39
|
+
`!!` are accepted syntax, but conversion failures and implicit coercions
|
|
40
|
+
remain undecided.
|
|
41
|
+
- **Named constants:** Are they needed, and how should they be represented,
|
|
42
|
+
scoped, and distinguished from external data?
|
|
43
|
+
- **Custom operator extensions:** The registry describes build-time syntax.
|
|
44
|
+
Runtime operator registration, scoping, and versioning remain unspecified.
|
|
45
|
+
- **Evaluation and compatibility:** Lookup, truthiness, arithmetic, equality,
|
|
46
|
+
collection scope, short-circuiting, and errors require decisions. The
|
|
47
|
+
no-fallback `if` result is settled as `null`.
|
|
48
|
+
|
|
49
|
+
## Next steps
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
Settle semantic typing, conversions, and runtime compatibility before building
|
|
53
|
+
an evaluator. For each syntax change, update the relevant operator file, regenerate the
|
|
54
|
+
schemas, and add accepted and rejected examples.
|
|
55
|
+
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Developing Gramson
|
|
2
|
+
|
|
3
|
+
## Files
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
- `spec/language.yaml`: selects the operator directories included in the language.
|
|
7
|
+
- `spec/core/*.yaml`: operator syntax, arities, names, titles, and summaries,
|
|
8
|
+
grouped into data, logic, comparison, arithmetic, collection, string, and debugging.
|
|
9
|
+
Longer descriptions are optional.
|
|
10
|
+
- `spec/extensions/*.yaml`: reusable optional extensions, selected explicitly by
|
|
11
|
+
manifests through `include` (currently `gramson:bitwise`).
|
|
12
|
+
- `gramson/generation.py`: loads and composes specifications and generates schemas.
|
|
13
|
+
- `tools/generate_schema.py`: repository generation entry point used by `make generate`.
|
|
14
|
+
- `gramson/cli.py`: generates grammars and validates JSON or YAML expressions.
|
|
15
|
+
- `pyproject.toml`: package metadata, runtime dependencies, and the `gramson` command.
|
|
16
|
+
- `schema/expression.schema.yaml`: generated language entry point.
|
|
17
|
+
- `schema/core/*.schema.yaml`: generated named operator definitions, mirroring
|
|
18
|
+
the source directories and file base names.
|
|
19
|
+
- `schema/shared.schema.yaml`: generated shared definitions such as `scalarLiteral`.
|
|
20
|
+
- `docs/json-value.schema.yaml`: hand-written result-data grammar.
|
|
21
|
+
- `examples/valid.json` and `examples/invalid.json`: accepted and rejected
|
|
22
|
+
expressions; only each entry's `expression` is validated.
|
|
23
|
+
- `tests/test_validation.py`: metaschema and example validation.
|
|
24
|
+
- `tests/test_jsonlogic.py`: offline syntax compatibility checks against the
|
|
25
|
+
pinned upstream [JSON Logic test corpus](../tests/vendor/jsonlogic/README.md),
|
|
26
|
+
with its MIT license, provenance, checksums, and compatibility expectations.
|
|
27
|
+
- `tests/test_generation.py`: generation freshness, operator coverage, arities,
|
|
28
|
+
shorthand, documentation, and registry validation.
|
|
29
|
+
- `tests/test_language.py`: directory selection, deterministic composition,
|
|
30
|
+
collision diagnostics, mirrored output, and cross-group recursion.
|
|
31
|
+
- `tests/test_openapi.py`: external references, request validation, and result
|
|
32
|
+
validation against the hand-written API contract.
|
|
33
|
+
- `docs/openapi.yaml`: hand-written placeholder `POST /evaluate` specification;
|
|
34
|
+
its only schema components reference `expression` and `jsonValue`.
|
|
35
|
+
- `docs/index.html`: Swagger viewer; see [viewing instructions](README.md).
|
|
36
|
+
- `requirements-dev.txt`: generation, validation, YAML-reading, and Ruff linting dependencies.
|
|
37
|
+
|
|
38
|
+
## Layout and composition
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
```text
|
|
42
|
+
spec/ schema/
|
|
43
|
+
language.yaml expression.schema.yaml
|
|
44
|
+
shared.schema.yaml
|
|
45
|
+
core/ core/
|
|
46
|
+
arithmetic.yaml arithmetic.schema.yaml
|
|
47
|
+
logic.yaml logic.schema.yaml
|
|
48
|
+
... ...
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
The repository manifest currently enables only its local `core`. Additional directories can be
|
|
52
|
+
enabled explicitly; operator sources are scanned recursively and sorted.
|
|
53
|
+
Duplicate operator keys or `schemaName` values are errors with both source
|
|
54
|
+
paths reported. Each group references the root expression grammar, allowing
|
|
55
|
+
nested expressions from every enabled group. See the specification guide for
|
|
56
|
+
manifest rules and custom output locations.
|
|
57
|
+
|
|
58
|
+
## Generate and validate
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
Requires Python 3.10 or newer. From the repository root:
|
|
62
|
+
|
|
63
|
+
```sh
|
|
64
|
+
make setup
|
|
65
|
+
make generate
|
|
66
|
+
make check
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Run `make` or `make help` to list targets:
|
|
70
|
+
|
|
71
|
+
| Target | Purpose |
|
|
72
|
+
| --- | --- |
|
|
73
|
+
| `setup` | Create `.venv` and install the CLI and development dependencies. |
|
|
74
|
+
| `generate` | Generate the expression, shared, and operator JSON Schemas. |
|
|
75
|
+
| `example` | Generate and validate the included core extension example. |
|
|
76
|
+
| `example-bitwise` | Generate and validate the bitwise extension example. |
|
|
77
|
+
| `example-external` | Generate and validate a user-owned namespace include. |
|
|
78
|
+
| `lint` | Run Python and OpenAPI linting. |
|
|
79
|
+
| `lint-python` | Check Python code and import ordering with Ruff. |
|
|
80
|
+
| `lint-openapi` | Validate the hand-written OpenAPI document and external references with [openapi-spec-validator](https://openapi-spec-validator.readthedocs.io/en/stable/cli.html). |
|
|
81
|
+
| `test` | Run all tests, including schema validation and generation freshness. |
|
|
82
|
+
| `check` | Run lint and tests without rewriting generated files. |
|
|
83
|
+
| `setup-release` | Install development and release tooling. |
|
|
84
|
+
| `build` | Build a source archive and a wheel from that source archive. |
|
|
85
|
+
| `check-dist` | Build and check metadata, bundled schemas, and the installed CLI. |
|
|
86
|
+
| `serve` | Regenerate schemas and serve the Swagger viewer at `http://127.0.0.1:8765/docs/`. |
|
|
87
|
+
|
|
88
|
+
Override `PYTHON` or `VENV` for setup, or `HOST` and `PORT` for the viewer,
|
|
89
|
+
for example `make serve PORT=8000`. Run `make setup` before other targets.
|
|
90
|
+
The OpenAPI wrapper and JSON-value schema remain hand-written; `make generate`
|
|
91
|
+
only rebuilds the expression grammar. `make check` fails when generated schemas
|
|
92
|
+
are stale, so regenerate and commit them after changing operator sources.
|
|
93
|
+
|
|
94
|
+
[GitHub Actions](../.github/workflows/check.yml) runs `make check` on every push and
|
|
95
|
+
pull request with Python 3.10 and 3.14. CI checks the committed schemas without
|
|
96
|
+
regenerating them.
|
|
97
|
+
It also builds and verifies the wheel and source distribution. The
|
|
98
|
+
[release guide](RELEASING.md) documents local checks and the PyPI Trusted
|
|
99
|
+
Publishing setup. Publishing a matching GitHub release triggers the separate
|
|
100
|
+
publication workflow; routine pushes and pull requests only build and check.
|
|
101
|
+
|
|
102
|
+
Python linting uses Ruff, configured in `pyproject.toml` for Python 3.10 with
|
|
103
|
+
error, unused-code, and import-order checks. `make lint-python` checks without
|
|
104
|
+
rewriting files. To apply safe fixes locally, run `.venv/bin/python -m ruff check . --fix`.
|
|
105
|
+
Ruff is a development dependency; the installed CLI does not require it.
|
|
106
|
+
|
|
107
|
+
Generation uses PyYAML to read and write YAML. Tests use local schemas and bundled
|
|
108
|
+
metaschema support, with no remote schema fetches. The expression, shared, and grouped operator schemas are
|
|
109
|
+
generated; edit the OpenAPI and JSON-value schemas directly when their contracts
|
|
110
|
+
change. Tests assert syntax and data shape, not evaluation results.
|
|
111
|
+
|