Skip to content

feat(ext): add tree-sitter grammar and refresh TextMate grammar 🌳 - #165

Merged
timfennis merged 6 commits into
masterfrom
feature/tree-sitter-grammar
May 30, 2026
Merged

feat(ext): add tree-sitter grammar and refresh TextMate grammar 🌳#165
timfennis merged 6 commits into
masterfrom
feature/tree-sitter-grammar

Conversation

@timfennis

Copy link
Copy Markdown
Owner

Context

Editors like Neovim, Helix, and Zed highlight via tree-sitter, not TextMate — the existing VS Code extension only ships a TextMate grammar, so those editors had no support. While adding tree-sitter, the TextMate grammar also turned out to predate the static-type work and had a couple of regex bugs.

Changes

New ext/tree-sitter-andy-cpp/ package

  • Full grammar (grammar.js) with a precedence ladder mirroring ndc_lexer/ndc_parser, plus committed generated src/ so consumers build without the CLI.
  • Highlight / locals / injection queries and a test/corpus.
  • README with branch-independent Neovim + Helix setup (built-in tree-sitter runtime) and optional ndc lsp wiring.
  • Validated against the full functional-test corpus: every valid .ndc program parses; only the deliberate // expect-error: cases fail.

TextMate grammar refresh (ext/andy-cpp)

  • Highlight type annotations: built-in type names, the -> return arrow, and : separators (the whole typecheck feature was previously invisible).
  • Tokenize parameter names and types inside function signatures (the fn … block had no inner patterns).
  • Detect continue and NaN.
  • Fix augmented assignment: ^= was mis-detected (\|\^) and \= was missing.
  • CHANGELOG entry under [Unreleased].

Notes for reviewers

  • The grammar commits generated src/parser.c; regenerate with tree-sitter generate after editing grammar.js.
  • Known limitations are documented in the package README (raw strings with embedded quotes, doubly-nested generics List<List<Int>>, named augmented assignment acc max= x). None occur in the current corpus.
  • VS Code keeps using the TextMate grammar — tree-sitter does not replace it.

🤖

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a47a9d2968

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ext/tree-sitter-andy-cpp/grammar.js
Named op-assign like `x multiply= 10` is valid Andy (the lexer glues an
identifier to a single `=` not followed by `=`), but the grammar only
accepted symbolic compound operators, so it silently mis-parsed the
construct as two statements. Add an external scanner that recognises the
glued identifier-equals token with the one char of lookahead the lexer
uses, so `a==b` equality is unaffected. Reported by Codex on #165.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d726d59d50

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ext/tree-sitter-andy-cpp/queries/locals.scm
The locals query only treated a direct `identifier` name/pattern as a
definition, so identifiers inside `let [a, b] = …`, `let (a, b) = …`,
`let a, b = …` and nested for-patterns like `for (x, y), [a, b, c] in …`
fell through to `@local.reference`, losing definition highlighting and
go-to-definition. Descend through the list/tuple/pattern_sequence
containers (up to two levels) for let, for, and destructured parameters.
Reported by Codex on #165.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5e0425ff94

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ext/tree-sitter-andy-cpp/grammar.js Outdated
A list comprehension may yield a bare comma tuple, e.g.
`[x, y for x in 1..10, y in 1..10]`. The grammar only accepted a single
expression before the comprehension clauses, so the comma forced the
list-literal path and the following `for` became an ERROR. Accept
`_expression_or_sequence` as the body, mirroring how the parser parses a
tuple before checking for `for`. Reported by Codex on #165.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bc27fc676e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ext/tree-sitter-andy-cpp/src/scanner.c Outdated
Codex flagged a possible issue where the external scanner advancing past
`and`/`or`/`in` before returning false could drop the keyword. In
practice tree-sitter resets the lexer to the pre-scan position on a false
return, so `a and b not in c` parses correctly. Add a corpus test that
locks this in. No scanner change needed.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a7a123ed6f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ext/tree-sitter-andy-cpp/grammar.js Outdated
Raw strings use a `#` fence specifically to embed quotes, e.g.
`r#"with "" quotes"#` and `r###"... r#"x"# ..."###` from the manual. The
regex token stopped at the first inner `"`, breaking the rest of the
literal. Recognise raw strings in the external scanner, which counts the
opening `#` run and scans until a `"` followed by exactly that many `#`.
The leading `r` is disambiguated from identifiers (and named op-assign)
by the character after it. Reported by Codex on #165.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: de583e9c02

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread ext/tree-sitter-andy-cpp/src/scanner.c
@timfennis
timfennis merged commit 5d1e8af into master May 30, 2026
1 check passed
@timfennis
timfennis deleted the feature/tree-sitter-grammar branch May 30, 2026 14:06
timfennis added a commit that referenced this pull request May 30, 2026
## Context

Follow-up to #165. Setting up the grammar in Helix surfaced two problems
(Neovim was unaffected):

1. **No highlighting in Helix.** Helix compiles `highlights` +
`injections` + `locals` into one tree-sitter query, so the Neovim-only
`#lua-match?` predicate in `injections.scm` failed the *entire*
highlight compile (`unknown predicate #lua-match?`). The LSP still
worked, which is why it looked like only highlighting was broken.
2. **Broken Helix setup docs.** The README's `[[grammar]]` git source
omitted `rev`, producing `data did not match any variant of untagged
enum GrammarSource`.

## Changes

- **`injections.scm`**: `#lua-match?` → `#match?`. Both Neovim and Helix
support `#match?`, and `^#!` matches identically under each editor's
regex. Verified the combined query now compiles via the `tree-sitter
highlight` CLI (same `tree-sitter-highlight` crate Helix uses), and that
Neovim still loads the query.
- **README Helix section**: git sources now include `rev` (with a
local-path alternative), and the build step uses `tree-sitter build -o
…/grammars/andy-cpp.so` instead of `hx --grammar build` (which rebuilds
every grammar and needs the output dir to pre-exist). Added an `hx
--health` check and an `hx`-vs-`helix` binary note.

Capture ordering was checked and left as-is: Helix's own bundled queries
place the catch-all `(identifier) @variable` before the specific
`@function`/`@variable.parameter` captures, confirming Helix resolves
overlaps last-match-wins like Neovim, so the existing ordering is
correct for both.

🤖

Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant