cypherast 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.
Files changed (76) hide show
  1. cypherast-0.1.0/.agents/rules/architecture.md +29 -0
  2. cypherast-0.1.0/.agents/rules/graph-native-naming.md +32 -0
  3. cypherast-0.1.0/.agents/rules/testing.md +22 -0
  4. cypherast-0.1.0/.github/copilot-instructions.md +74 -0
  5. cypherast-0.1.0/.github/instructions/python.instructions.md +12 -0
  6. cypherast-0.1.0/.github/instructions/review.instructions.md +24 -0
  7. cypherast-0.1.0/.github/workflows/ci.yml +87 -0
  8. cypherast-0.1.0/.github/workflows/release.yml +64 -0
  9. cypherast-0.1.0/.gitignore +45 -0
  10. cypherast-0.1.0/.python-version +1 -0
  11. cypherast-0.1.0/AGENTS.md +102 -0
  12. cypherast-0.1.0/CONTRIBUTING.md +46 -0
  13. cypherast-0.1.0/HOWTOAI.md +60 -0
  14. cypherast-0.1.0/LICENSE +21 -0
  15. cypherast-0.1.0/Makefile +115 -0
  16. cypherast-0.1.0/PKG-INFO +230 -0
  17. cypherast-0.1.0/README.md +201 -0
  18. cypherast-0.1.0/cypherast/__init__.py +214 -0
  19. cypherast-0.1.0/cypherast/ast.py +604 -0
  20. cypherast-0.1.0/cypherast/cli.py +62 -0
  21. cypherast-0.1.0/cypherast/dialects/__init__.py +23 -0
  22. cypherast-0.1.0/cypherast/dialects/capabilities.py +35 -0
  23. cypherast-0.1.0/cypherast/dialects/constraints.py +425 -0
  24. cypherast-0.1.0/cypherast/dialects/dialect.py +142 -0
  25. cypherast-0.1.0/cypherast/dialects/memgraph.py +34 -0
  26. cypherast-0.1.0/cypherast/dialects/neo4j.py +30 -0
  27. cypherast-0.1.0/cypherast/dialects/opencypher.py +25 -0
  28. cypherast-0.1.0/cypherast/dialects/puppygraph.py +54 -0
  29. cypherast-0.1.0/cypherast/errors.py +158 -0
  30. cypherast-0.1.0/cypherast/executor/__init__.py +6 -0
  31. cypherast-0.1.0/cypherast/executor/engine.py +680 -0
  32. cypherast-0.1.0/cypherast/executor/env.py +308 -0
  33. cypherast-0.1.0/cypherast/executor/graph.py +112 -0
  34. cypherast-0.1.0/cypherast/lexer.py +467 -0
  35. cypherast-0.1.0/cypherast/lineage.py +186 -0
  36. cypherast-0.1.0/cypherast/optimizer/__init__.py +67 -0
  37. cypherast-0.1.0/cypherast/optimizer/catalog.py +96 -0
  38. cypherast-0.1.0/cypherast/optimizer/engine.py +126 -0
  39. cypherast-0.1.0/cypherast/parser.py +880 -0
  40. cypherast-0.1.0/cypherast/planner/__init__.py +30 -0
  41. cypherast-0.1.0/cypherast/planner/cache.py +32 -0
  42. cypherast-0.1.0/cypherast/planner/cost.py +36 -0
  43. cypherast-0.1.0/cypherast/planner/enumeration.py +122 -0
  44. cypherast-0.1.0/cypherast/planner/physical_ops.py +162 -0
  45. cypherast-0.1.0/cypherast/planner/plan.py +162 -0
  46. cypherast-0.1.0/cypherast/py.typed +0 -0
  47. cypherast-0.1.0/cypherast/renderer.py +431 -0
  48. cypherast-0.1.0/cypherast/rewriter/__init__.py +62 -0
  49. cypherast-0.1.0/cypherast/rewriter/annotate_types.py +39 -0
  50. cypherast-0.1.0/cypherast/rewriter/canonicalize_patterns.py +21 -0
  51. cypherast-0.1.0/cypherast/rewriter/merge_match_chains.py +63 -0
  52. cypherast-0.1.0/cypherast/rewriter/pushdown_predicates.py +77 -0
  53. cypherast-0.1.0/cypherast/rewriter/qualify.py +40 -0
  54. cypherast-0.1.0/cypherast/rewriter/simplify.py +84 -0
  55. cypherast-0.1.0/cypherast/schema.py +105 -0
  56. cypherast-0.1.0/cypherast/scope.py +113 -0
  57. cypherast-0.1.0/docs/api.md +174 -0
  58. cypherast-0.1.0/pyproject.toml +86 -0
  59. cypherast-0.1.0/tests/__init__.py +0 -0
  60. cypherast-0.1.0/tests/tck/__init__.py +0 -0
  61. cypherast-0.1.0/tests/tck/features/CreateAcceptance.feature +53 -0
  62. cypherast-0.1.0/tests/tck/features/ExpressionAcceptance.feature +70 -0
  63. cypherast-0.1.0/tests/tck/features/MatchAcceptance.feature +44 -0
  64. cypherast-0.1.0/tests/tck/features/ReturnAcceptance.feature +51 -0
  65. cypherast-0.1.0/tests/tck/runner.py +161 -0
  66. cypherast-0.1.0/tests/tck/test_tck_scoreboard.py +24 -0
  67. cypherast-0.1.0/tests/tck/test_tck_smoke.py +19 -0
  68. cypherast-0.1.0/tests/test_executor.py +59 -0
  69. cypherast-0.1.0/tests/test_hardening.py +82 -0
  70. cypherast-0.1.0/tests/test_lexer.py +32 -0
  71. cypherast-0.1.0/tests/test_optimizer_rules.py +90 -0
  72. cypherast-0.1.0/tests/test_parse.py +62 -0
  73. cypherast-0.1.0/tests/test_planner_lineage.py +24 -0
  74. cypherast-0.1.0/tests/test_puppygraph_dialect.py +117 -0
  75. cypherast-0.1.0/tests/test_rewriter.py +36 -0
  76. cypherast-0.1.0/uv.lock +354 -0
@@ -0,0 +1,29 @@
1
+ ---
2
+ description: Pipeline layers and module boundaries for cypherast
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # Architecture
7
+
8
+ Pipeline order (do not skip layers or import backwards):
9
+
10
+ 1. **Lexer / parser** (`lexer.py`, `parser.py`) → AST (`ast.py`)
11
+ 2. **Dialect** (`dialects/`) — parse/render hooks, `capabilities`, constraint rewrites
12
+ 3. **Rewriter passes** (`rewriter/`) — pure AST transforms
13
+ 4. **Optimizer** (`optimizer/`) — named `Rule` / `RuleSet`, catalogs, `only`/`disable`
14
+ 5. **Planner** (`planner/`) — explain / cost / physical ops
15
+ 6. **Executor** (`executor/`) — in-memory `Graph` + engine
16
+
17
+ ## Rules
18
+
19
+ - Public surface stays in `cypherast/__init__.py` (+ CLI). New user-facing entry points go there.
20
+ - Dialect-specific engine limits belong in `DialectCapabilities` + `constraints.py` / `constraint_rules`, not hard-coded label/rel names from one customer graph.
21
+ - Rewrite implementations live in `rewriter/`; registration/order/names live in `optimizer/catalog.py`.
22
+ - Rendering goes through `Renderer` / `AstNode.cypher(...)` — keep dialect emit differences in dialect renderer subclasses when needed.
23
+ - Avoid circular imports: `rewriter/__init__.py` must not import `optimizer` at module top level (optimizer catalog imports rewriter pass modules).
24
+
25
+ ## Anti-patterns
26
+
27
+ - SQL vocabulary or SQL-toolchain brand names in APIs/docs
28
+ - Putting PuppyGraph (or any engine) hacks into shared openCypher paths without a capability flag
29
+ - Enabling `merge_match_chains` by default
@@ -0,0 +1,32 @@
1
+ ---
2
+ description: Keep cypherast graph-native — no SQL-clone framing or foreign brand names
3
+ alwaysApply: true
4
+ ---
5
+
6
+ # Graph-native naming
7
+
8
+ cypherast is a **Cypher/GQL** library. Do not frame it as a SQL toolchain clone in code, comments, docs, tests, READMEs, commit messages, or user-facing copy. Do not cite third-party SQL library brand names.
9
+
10
+ ## Do
11
+
12
+ - Describe features in graph/Cypher terms: named rewrite rules, dialects, `only`/`disable`, capability constraints.
13
+ - Use graph-native vocabulary (MATCH, pattern, binding, hop).
14
+
15
+ ## Don't
16
+
17
+ ```text
18
+ # BAD
19
+ "SQL-style optimizer like <sql library>"
20
+ "transpile_sql" / AstNode.sql()
21
+ customer- or employer-specific product codenames in public docs
22
+ ```
23
+
24
+ ```text
25
+ # GOOD
26
+ "named rule filters"
27
+ "enable/disable by rule name"
28
+ AstNode.cypher()
29
+ generic engine capability limits
30
+ ```
31
+
32
+ Keep the idea; drop foreign brand names and internal org jargon from the public repo.
@@ -0,0 +1,22 @@
1
+ ---
2
+ description: Pytest conventions for cypherast
3
+ globs: tests/**/*.py
4
+ alwaysApply: false
5
+ ---
6
+
7
+ # Testing
8
+
9
+ - Framework: **pytest** (`tests/`, `testpaths` in `pyproject.toml`).
10
+ - Name files `tests/test_*.py`. Prefer focused cases over giant fixtures.
11
+ - Public API tests go through `import cypherast` (`parse_one`, `optimize`, `translate`, `validate`).
12
+ - Dialect capability tests: `tests/test_puppygraph_dialect.py`.
13
+ - Named rule filters: `tests/test_optimizer_rules.py`.
14
+ - Parser/hardening: `tests/test_parse.py`, `tests/test_hardening.py`.
15
+ - TCK parse-rate: `tests/tck/` via `make test-tck` — do not treat sample `.feature` files as a full openCypher TCK.
16
+
17
+ ## Rules
18
+
19
+ - Assert on rendered Cypher (`.cypher(...)`) or structured AST — not private rewriter helpers unless unit-testing that helper.
20
+ - When testing `only`/`disable`/`constraint_disable`, name the rule exactly as in `RULES.names` / `constraint_rule_set().names`.
21
+ - After dialect constraint changes, keep `make test-puppy` green.
22
+ - No network / live graph engines in unit tests — use in-memory `Graph` or AST-only checks.
@@ -0,0 +1,74 @@
1
+ # GitHub Copilot — repository-wide instructions (chat, coding agent, code review)
2
+
3
+ ## What this repo is
4
+
5
+ **cypherast** is a Cypher/GQL library (lexer → parser → AST → named rewrite rules → planner → in-memory executor).
6
+ Python 3.13+, zero runtime dependencies, MIT. Public API lives in `cypherast/__init__.py`.
7
+
8
+ This is a **graph query** toolchain. Use Cypher/GQL vocabulary (MATCH, pattern, binding, hop, dialect). Do not frame APIs or reviews in SQL terms.
9
+
10
+ For deeper agent context see root `AGENTS.md`. Prefer that file over inventing architecture.
11
+
12
+ ## When reviewing a PR
13
+
14
+ Flag as blockers:
15
+
16
+ 1. New runtime dependencies without clear justification
17
+ 2. SQL-shaped public names (`sql()`, `transpile_sql`, etc.) or docs that describe the library as a SQL clone
18
+ 3. Enabling `merge_match_chains` in default `RULES` (Cartesian risk on some engines)
19
+ 4. Hard-coding customer-specific graph labels/rel types into dialect constraint code — use `DialectCapabilities` / generic rules
20
+ 5. Qualifying anonymous variables inside pattern predicates (engines reject new bindings there)
21
+ 6. Breaking `AstNode.cypher(...)` / `cypher=` public naming
22
+ 7. Skipping tests for parser, rewriter, optimizer rule, or dialect capability changes
23
+ 8. Lowering coverage gate or silencing CI (`|| true` on mypy/tests) without discussion
24
+
25
+ Nits (non-blocking unless pervasive):
26
+
27
+ - Missing type hints on new public functions
28
+ - Rewriter pass registered in the wrong order vs `optimizer/catalog.py`
29
+ - Docs/README not updated when public API changed
30
+
31
+ ## Architecture map (for understanding diffs)
32
+
33
+ | Path | Owns |
34
+ |------|------|
35
+ | `cypherast/lexer.py`, `parser.py`, `ast.py` | Tokenize / parse / IR |
36
+ | `cypherast/dialects/` | Dialect registry, capabilities, constraint rewrites |
37
+ | `cypherast/rewriter/` | Individual AST rewrite passes |
38
+ | `cypherast/optimizer/` | Named `Rule` / `RuleSet`, catalogs, `only`/`disable` |
39
+ | `cypherast/planner/` | Explain / cost / plans |
40
+ | `cypherast/executor/` | In-memory `Graph` + execute |
41
+ | `cypherast/renderer.py` | AST → Cypher text |
42
+ | `tests/` | pytest; `tests/tck/` = sample parse-rate features only |
43
+
44
+ Pipeline order: parse → (optional) optimize/constraints → render / plan / run.
45
+
46
+ ## Dialects & optimize
47
+
48
+ - Dialects: `opencypher`, `neo4j`, `memgraph`, `puppygraph`
49
+ - `optimize(..., write=)` applies canonicalizer rules then write-dialect constraints
50
+ - Filters: `only` / `disable` (canonicalizer), `constraint_only` / `constraint_disable`
51
+ - Default rules: `qualify` → `canonicalize_patterns` → `simplify` → `pushdown_predicates` → `annotate_types`
52
+ - Opt-in: `OPTIONAL_RULES` includes `merge_match_chains`
53
+
54
+ ## How to verify locally (tell authors)
55
+
56
+ ```bash
57
+ uv sync --group dev
58
+ make check # ruff + mypy + pytest
59
+ make test-cov # coverage XML + term
60
+ make test-puppy # PuppyGraph dialect tests
61
+ ```
62
+
63
+ CI workflows:
64
+
65
+ - `.github/workflows/ci.yml` — ruff, mypy (strict), pytest with `--cov-fail-under=60`, uploads `coverage.xml`
66
+ - `.github/workflows/release.yml` — tag `v*` → verify version → build → PyPI (OIDC) → GitHub Release
67
+
68
+ ## Coding conventions for suggestions
69
+
70
+ - Match existing style: ruff line-length 100, mypy strict, Python 3.13
71
+ - Prefer small, test-backed changes over large refactors
72
+ - New rewrite logic → implement under `rewriter/`, register name in `optimizer/catalog.py`
73
+ - New engine limits → `DialectCapabilities` + named constraint rule, not one-off string hacks
74
+ - Keep comments/docs free of SQL-toolchain brand names
@@ -0,0 +1,12 @@
1
+ ---
2
+ applyTo: "**/*.py"
3
+ ---
4
+
5
+ # Python review focus (cypherast)
6
+
7
+ - Prefer graph/Cypher naming in identifiers and user-facing strings.
8
+ - Public API changes must update `cypherast/__init__.py` exports and usually `docs/api.md` / `README.md`.
9
+ - Avoid circular imports: do not import `cypherast.optimizer` at the top of `cypherast/rewriter/__init__.py`.
10
+ - Dialect constraint callables should accept `(tree, schema=None)` so `RuleSet.apply` can pass kwargs.
11
+ - Tests: use `cypherast.parse_one` / `optimize` / `translate` / `validate`; assert via `.cypher(...)` when checking emit.
12
+ - Typecheck is strict (`mypy` on `cypherast` package) — new public functions need annotations.
@@ -0,0 +1,24 @@
1
+ ---
2
+ applyTo: ".github/workflows/**/*.yml,cypherast/dialects/**/*.py,cypherast/optimizer/**/*.py,cypherast/rewriter/**/*.py"
3
+ ---
4
+
5
+ # High-risk paths — Copilot code review
6
+
7
+ When reviewing changes under dialects, optimizer, rewriter, or CI workflows:
8
+
9
+ ## Dialects / constraints
10
+
11
+ - Confirm capability flags match rewrite behavior (`require_limit_on_row_return`, `allow_cartesian_match_paths`, var-length bounds).
12
+ - Do not reintroduce default MATCH-chain merge or `exists()` emit for PuppyGraph unless capabilities explicitly allow it.
13
+ - Unbounded `*` var-length must remain allowed when `allow_unbounded_var_length=True` and `max_var_length_hops is None`.
14
+
15
+ ## Optimizer / rewriter
16
+
17
+ - Rule names in `only`/`disable` must match catalog names exactly.
18
+ - Keep `merge_match_chains` in `OPTIONAL_RULES` only unless the PR deliberately changes that policy and updates docs/tests.
19
+
20
+ ## CI
21
+
22
+ - Do not drop coverage fail-under without calling it out in the PR.
23
+ - Do not make mypy or pytest non-blocking again.
24
+ - Coverage runs in the CI `test` job and must still produce `coverage.xml` artifact on failure when possible.
@@ -0,0 +1,87 @@
1
+ # CI — lint, types, tests + coverage
2
+
3
+ name: CI
4
+
5
+ on:
6
+ push:
7
+ branches: [main, master]
8
+ pull_request:
9
+
10
+ concurrency:
11
+ group: ci-${{ github.workflow }}-${{ github.ref }}
12
+ cancel-in-progress: true
13
+
14
+ jobs:
15
+ lint:
16
+ name: Lint (ruff)
17
+ runs-on: ubuntu-latest
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+ - uses: astral-sh/setup-uv@v5
21
+ with:
22
+ enable-cache: true
23
+ - name: Sync deps
24
+ run: uv sync --group dev
25
+ - name: Ruff
26
+ run: uv run ruff check cypherast tests
27
+
28
+ typecheck:
29
+ name: Typecheck (mypy)
30
+ runs-on: ubuntu-latest
31
+ steps:
32
+ - uses: actions/checkout@v4
33
+ - uses: astral-sh/setup-uv@v5
34
+ with:
35
+ enable-cache: true
36
+ - name: Sync deps
37
+ run: uv sync --group dev
38
+ - name: Mypy (strict)
39
+ run: uv run mypy cypherast
40
+
41
+ test:
42
+ name: Tests + coverage
43
+ runs-on: ubuntu-latest
44
+ strategy:
45
+ fail-fast: false
46
+ matrix:
47
+ python-version: ["3.13"]
48
+ steps:
49
+ - uses: actions/checkout@v4
50
+ - uses: astral-sh/setup-uv@v5
51
+ with:
52
+ enable-cache: true
53
+ python-version: ${{ matrix.python-version }}
54
+ - name: Sync deps
55
+ run: uv sync --group dev
56
+ - name: Pytest + coverage
57
+ run: >
58
+ uv run pytest
59
+ --cov=cypherast
60
+ --cov-report=term-missing:skip-covered
61
+ --cov-report=xml:coverage.xml
62
+ --cov-fail-under=60
63
+ - name: PuppyGraph dialect suite
64
+ run: uv run pytest tests/test_puppygraph_dialect.py -q
65
+ - name: Upload coverage.xml
66
+ if: always()
67
+ uses: actions/upload-artifact@v4
68
+ with:
69
+ name: coverage-xml
70
+ path: coverage.xml
71
+ if-no-files-found: warn
72
+ retention-days: 14
73
+ - name: Coverage summary (PR)
74
+ if: github.event_name == 'pull_request'
75
+ run: |
76
+ python - <<'PY'
77
+ import xml.etree.ElementTree as ET
78
+ from pathlib import Path
79
+ p = Path("coverage.xml")
80
+ if not p.exists():
81
+ print("coverage.xml missing")
82
+ raise SystemExit(0)
83
+ root = ET.parse(p).getroot()
84
+ line_rate = float(root.attrib.get("line-rate", "0"))
85
+ pct = round(line_rate * 100, 1)
86
+ print(f"Line coverage: {pct}% (fail-under 60%)")
87
+ PY
@@ -0,0 +1,64 @@
1
+ # Release — build + publish to PyPI on version tags (v*)
2
+
3
+ name: Release
4
+
5
+ on:
6
+ push:
7
+ tags:
8
+ - "v*"
9
+
10
+ permissions:
11
+ contents: write # GitHub Release
12
+ id-token: write # PyPI trusted publishing (OIDC)
13
+
14
+ concurrency:
15
+ group: release-${{ github.ref }}
16
+ cancel-in-progress: false
17
+
18
+ jobs:
19
+ release:
20
+ name: Build, verify, publish
21
+ runs-on: ubuntu-latest
22
+ environment: pypi
23
+ steps:
24
+ - uses: actions/checkout@v4
25
+
26
+ - uses: astral-sh/setup-uv@v5
27
+ with:
28
+ enable-cache: true
29
+ python-version: "3.13"
30
+
31
+ - name: Sync deps
32
+ run: uv sync --group dev
33
+
34
+ - name: Tag must match pyproject version
35
+ run: |
36
+ TAG="${GITHUB_REF_NAME#v}"
37
+ PKG=$(uv run python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
38
+ echo "tag=$TAG pyproject=$PKG"
39
+ test "$TAG" = "$PKG" || {
40
+ echo "::error::Tag v$TAG does not match project.version=$PKG in pyproject.toml"
41
+ exit 1
42
+ }
43
+
44
+ - name: Quality gate
45
+ run: |
46
+ uv run ruff check cypherast tests
47
+ uv run mypy cypherast
48
+ uv run pytest --cov=cypherast --cov-fail-under=60 -q
49
+
50
+ - name: Build sdist + wheel
51
+ run: uv build
52
+
53
+ - name: Check dist
54
+ run: uvx twine check dist/*
55
+
56
+ - name: Publish to PyPI
57
+ run: uv publish
58
+
59
+ - name: GitHub Release
60
+ uses: softprops/action-gh-release@v2
61
+ with:
62
+ files: dist/*
63
+ generate_release_notes: true
64
+ fail_on_unmatched_files: true
@@ -0,0 +1,45 @@
1
+ # Python / tooling
2
+ .venv/
3
+ __pycache__/
4
+ *.py[cod]
5
+ *.egg-info/
6
+ .eggs/
7
+ build/
8
+ dist/
9
+ .pytest_cache/
10
+ .mypy_cache/
11
+ .ruff_cache/
12
+ .coverage
13
+ htmlcov/
14
+ coverage.xml
15
+ *.cover
16
+ .hypothesis/
17
+ .tox/
18
+ .nox/
19
+
20
+ # Secrets / credentials — never commit
21
+ .env
22
+ .env.*
23
+ !.env.example
24
+ *.pem
25
+ *.key
26
+ *.p12
27
+ *.pfx
28
+ id_rsa
29
+ id_rsa.*
30
+ id_ed25519
31
+ id_ed25519.*
32
+ *_secret*
33
+ *credentials*
34
+ *.token
35
+ .secrets/
36
+ secrets/
37
+
38
+ # Editor / OS / local tool state (not shared)
39
+ .DS_Store
40
+ .idea/
41
+ .vscode/
42
+ *.swp
43
+ *~
44
+ .claude/
45
+ .cursor/
@@ -0,0 +1 @@
1
+ 3.13
@@ -0,0 +1,102 @@
1
+ # cypherast — agent instructions
2
+
3
+ Cypher/GQL library: lexer → parser → AST → named rewrite rules → planner → in-memory executor.
4
+ Zero runtime deps. Python **3.13+**. Package version **0.1.0** (`pyproject.toml`). MIT.
5
+
6
+ Canonical AI instructions live here. Topic rules live under `.agents/rules/` only (no tool-specific stub/rule trees in-repo).
7
+
8
+ ## Non-negotiables
9
+
10
+ - Graph-native naming only (MATCH, pattern, binding, hop). No SQL vocabulary in public APIs or docs.
11
+ - Never frame the library as a SQL toolchain clone — see `.agents/rules/graph-native-naming.md`.
12
+ - Prefer `AstNode.cypher(...)` for render; public params use `cypher=`, not SQL-shaped names.
13
+ - Do not add runtime dependencies without an explicit request.
14
+ - Do not invent dialect capability limits — read `DialectCapabilities` / `cypherast/dialects/constraints.py`.
15
+ - Do not hard-code customer-specific graph labels/rel types into dialect code.
16
+ ## Public API (`cypherast/__init__.py`)
17
+
18
+ | Function | Role |
19
+ |----------|------|
20
+ | `parse` / `parse_one` | Cypher → AST (`read=` dialect) |
21
+ | `optimize` | Canonicalizer + optional write-dialect constraints (`only` / `disable` / `constraint_*`) |
22
+ | `translate` / `transpile` | Parse `from_` → render `to_`; `optimize=` applies target constraints |
23
+ | `validate` | List capability `ConstraintIssue`s for a dialect |
24
+ | `explain` / `profile` / `run` | Plan / profile / execute on in-memory `Graph` |
25
+ | `lineage` | Binding provenance |
26
+
27
+ CLI entry: `cypherast.cli:main` (`uv run cypherast …`).
28
+
29
+ ## Dialects
30
+
31
+ Registered under `cypherast/dialects/`: `opencypher`, `neo4j`, `memgraph`, `puppygraph`.
32
+ `Dialect.optimize` runs `cypherast.optimizer` `RULES` then `constraint_rules(capabilities)`.
33
+ PuppyGraph caps are generic engine limits (LIMIT, no Cartesian multi-path MATCH, etc.) — keep dialect code free of domain-specific label/rel names.
34
+
35
+ ## Optimizer rules
36
+
37
+ - Default `RULES`: `qualify` → `canonicalize_patterns` → `simplify` → `pushdown_predicates` → `annotate_types`
38
+ - Opt-in: `OPTIONAL_RULES` (`merge_match_chains`) — not default (Cartesian risk on some engines)
39
+ - Constraints built by `constraint_rules(caps)` — e.g. `ensure_row_limit`, `split_multi_path_match`
40
+
41
+ ## Dev commands (verified in `Makefile` / `pyproject.toml`)
42
+
43
+ ```bash
44
+ uv sync --group dev
45
+ make check # ruff + mypy + pytest
46
+ make test
47
+ make test-puppy
48
+ make optimize Q="MATCH (n:Person) RETURN n.name" WRITE=puppygraph
49
+ make optimize Q="..." DISABLE=qualify CONSTRAINT_DISABLE=ensure_row_limit
50
+ ```
51
+
52
+ CI:
53
+
54
+ - `.github/workflows/ci.yml` — ruff, mypy (strict), pytest + coverage (fail-under 60%, uploads `coverage.xml`)
55
+ - `.github/workflows/release.yml` — on `v*` tags: verify tag==version → check → `uv build` → `uv publish` (OIDC) → GitHub Release
56
+
57
+ Copilot: `.github/copilot-instructions.md` (+ `.github/instructions/*.instructions.md`).
58
+
59
+ ## Project Structure
60
+
61
+ Single Python package (uv + hatchling):
62
+
63
+ ```text
64
+ cypherast/
65
+ ├── AGENTS.md # this file (canonical AI instructions)
66
+ ├── HOWTOAI.md
67
+ ├── CONTRIBUTING.md
68
+ ├── README.md
69
+ ├── Makefile
70
+ ├── pyproject.toml
71
+ ├── LICENSE
72
+ ├── .agents/rules/ # agent topic rules (canonical)
73
+ │ ├── graph-native-naming.md
74
+ │ ├── architecture.md
75
+ │ └── testing.md
76
+ ├── .github/
77
+ │ ├── copilot-instructions.md
78
+ │ ├── instructions/ # path-scoped Copilot review rules
79
+ │ └── workflows/
80
+ │ ├── ci.yml # lint + mypy + pytest/coverage
81
+ │ └── release.yml # tag v* → PyPI + GitHub Release
82
+ ├── docs/api.md
83
+ ├── cypherast/
84
+ │ ├── __init__.py # public API
85
+ │ ├── ast.py
86
+ │ ├── lexer.py
87
+ │ ├── parser.py
88
+ │ ├── renderer.py
89
+ │ ├── cli.py
90
+ │ ├── errors.py
91
+ │ ├── schema.py
92
+ │ ├── scope.py
93
+ │ ├── lineage.py
94
+ │ ├── dialects/ # Dialect + capabilities + constraints
95
+ │ ├── rewriter/ # rewrite pass implementations
96
+ │ ├── optimizer/ # Rule / RuleSet / RULES catalog
97
+ │ ├── planner/
98
+ │ └── executor/ # in-memory Graph + engine
99
+ └── tests/
100
+ ├── test_*.py
101
+ └── tck/ # sample .feature parse-rate suite
102
+ ```
@@ -0,0 +1,46 @@
1
+ # Contributing to cypherast
2
+
3
+ ## Setup
4
+
5
+ ```bash
6
+ uv sync --group dev
7
+ make check
8
+ ```
9
+
10
+ Requires Python 3.13+ (`requires-python` in `pyproject.toml`).
11
+
12
+ ## Workflow
13
+
14
+ 1. Keep changes scoped — match existing module boundaries (`dialects`, `rewriter`, `optimizer`, `planner`, `executor`).
15
+ 2. Add or update tests under `tests/` for behavior changes.
16
+ 3. Run `make check` (ruff, mypy, pytest) before opening a PR.
17
+ 4. Prefer graph-native naming; see `AGENTS.md` and `.agents/rules/`.
18
+
19
+ ## Code style
20
+
21
+ - Ruff: line length 100, target `py313` (`pyproject.toml`).
22
+ - Mypy: `strict = true` on the `cypherast` package.
23
+ - Zero runtime dependencies unless explicitly agreed.
24
+
25
+ ## Tests
26
+
27
+ ```bash
28
+ make test
29
+ make test-cov
30
+ make test-puppy
31
+ make test-tck
32
+ ```
33
+
34
+ CI runs ruff, mypy (strict), and pytest with coverage (fail-under 60%) in one workflow.
35
+
36
+ ## Release / PyPI
37
+
38
+ 1. Bump `version` in `pyproject.toml`.
39
+ 2. Tag `vX.Y.Z` matching that version and push the tag.
40
+ 3. `.github/workflows/release.yml` builds, publishes via PyPI trusted publishing, opens a GitHub Release.
41
+
42
+ See README **Publishing to PyPI** for one-time publisher setup.
43
+
44
+ ## Docs
45
+
46
+ Update `README.md` / `docs/api.md` when the public API changes. Keep AI instructions in `AGENTS.md` (not duplicated into tool stubs).
@@ -0,0 +1,60 @@
1
+ # How to Use AI with cypherast
2
+
3
+ Practical guide for contributing with AI coding assistants.
4
+
5
+ ## Core principles
6
+
7
+ - Human owns the PR. Understand every line before merge.
8
+ - Match existing graph-native style: Cypher terms, zero runtime deps, typed Python 3.13.
9
+ - Read `AGENTS.md` and `.agents/rules/` before large changes.
10
+ - Never invent dialect limits — open `cypherast/dialects/capabilities.py` and `constraints.py`.
11
+
12
+ ## Best workflow
13
+
14
+ 1. Sync env: `uv sync --group dev`
15
+ 2. Reproduce with a minimal Cypher string via `parse_one` / `optimize` / `translate`
16
+ 3. Prefer TDD for parser/rewriter/dialect behavior (`tests/test_*.py`)
17
+ 4. Run `make check` (ruff + mypy + pytest) before claiming done
18
+ 5. For PuppyGraph emit path: also `make test-puppy` and `make validate Q="..."`
19
+
20
+ ### Useful Make targets
21
+
22
+ ```bash
23
+ make help
24
+ make optimize Q="MATCH (n:Person) WHERE n.status = 'ACTIVE' RETURN n.name"
25
+ make optimize Q="..." WRITE=puppygraph CONSTRAINT_DISABLE=ensure_row_limit
26
+ make translate Q="..." FROM=opencypher TO=puppygraph OPT=1
27
+ make validate Q="MATCH (n) RETURN n" DIALECT=puppygraph
28
+ ```
29
+
30
+ ### Named optimizer rules
31
+
32
+ ```python
33
+ import cypherast
34
+ from cypherast.optimizer import RULES, OPTIONAL_RULES
35
+
36
+ cypherast.optimize(q, disable=["qualify"])
37
+ cypherast.optimize(q, write="puppygraph", constraint_disable=["ensure_row_limit"])
38
+ cypherast.optimize(q, rules=RULES + OPTIONAL_RULES) # opt-in merge_match_chains
39
+ ```
40
+
41
+ ## What to watch
42
+
43
+ - Do not merge MATCH chains into comma paths by default — PuppyGraph rejects Cartesian multi-path MATCH.
44
+ - Pattern predicates must not introduce new bindings; do not qualify anon vars inside them.
45
+ - `AstNode.cypher(dialect=...)` for render checks after optimize/translate.
46
+ - Keep `merge_match_chains` out of default `RULES` unless explicitly opting in.
47
+
48
+ ## Review bar for AI-authored PRs
49
+
50
+ - Tests cover the behavior change (parser, rewriter, dialect, or API).
51
+ - Docs/README/api only updated when the public surface changed.
52
+ - No SQL-clone framing in comments or docs (see `.agents/rules/graph-native-naming.md`).
53
+ - `make check` green locally.
54
+
55
+ ## Tools
56
+
57
+ - Canonical instructions: `AGENTS.md`
58
+ - Shared rules: `.agents/rules/` only
59
+ - Copilot (optional): `.github/copilot-instructions.md`
60
+ - Full API samples: `docs/api.md`
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Gaurav Sagar
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.