Skip to content

Feat/721 vb6 grammar r2 - #2564

Open
Tokarzewski wants to merge 5 commits into
DeusData:mainfrom
Tokarzewski:feat/721-vb6-grammar-r2
Open

Tokarzewski wants to merge 5 commits into
DeusData:mainfrom
Tokarzewski:feat/721-vb6-grammar-r2

Conversation

@Tokarzewski

@Tokarzewski Tokarzewski commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

What does this PR do?

Adds Visual Basic 6 / VBA as a language (CBM_LANG_VB6) and models VB6 class modules as Class + Method nodes. This is part two of #721, following the merged short-term disambiguation in #2074. It supersedes my earlier #2086 (grammar) and #2087 (class modules), which I withdrew before review reached a decision; this is the same work rebased onto current main as a single branch. Happy to split it back into the two PRs, or to take only the grammar, whichever is easier to review.

Refs #721.

Two commits are the grammar and wiring, two are the class-module extraction and its tests:

Commit Scope
feat(lang): add Visual Basic 6 / VBA grammar (harumiWeb/tree-sitter-vba) grammar, enum, extraction hooks, registries, count surfaces
test(language): VB6 content rows now classify as CBM_LANG_VB6 one upstream test table that still expected the #2074 "unsupported" stub
feat(extract): VB6 class modules as Class + Methods synthesised class, method scope, call attribution
test(extract): VB6 class-module synthesis and Method call attribution regression + contract tests

Grammar

harumiWeb/tree-sitter-vba @ 63b2f8d0d65c (v0.13.0, MIT, ABI 15, lexer-only, no external scanner). It parses the module export headers natively (VERSION, Begin ... End, BeginProperty ... EndProperty, GUID blocks, Attribute lines), so the .frm designer block needs no preprocessing step. VB6-specific statements are covered (Declare, Implements, Event/RaiseEvent, WithEvents, Property Get|Let|Set). .cls/.frm are routed by the #2074 content sniffers (now returning CBM_LANG_VB6 instead of the unsupported marker); .bas/.ctl/.dsr/.pag map directly by extension.

Alternatives I rejected: arrmee-wt/tree-sitter-vba (GPL-3.0, outside scripts/license-policy.json), joannefan/tree-sitter-vb6 (no licence file, no generated parser), CodeAnt-AI/tree-sitter-vb-dotnet (VB.NET, which is #1073).

Provenance caveat, please decide. Upstream gitignores src/parser.c, so the vendored parser.c (about 46 MB) was generated from the pinned grammar.js with tree-sitter-cli 0.26.9 (tree-sitter generate --abi 15, the project's own pnpm generate). Byte-identity with an upstream artefact therefore cannot be claimed. MANIFEST.md records the exact regeneration command and marks the row NOT-IN-REGISTRIES (see note). The size comes from per-keyword case-insensitive expansion. This was the open question on the earlier PRs; I would rather you decide it explicitly than have me assume it is acceptable.

Extraction

  • Sub/Function/Property Get|Let|Set/Declare/Event become callables; Type ... End Type becomes a Class with typed Fields; Enum becomes an Enum with members; module-level Dim/Const become Variables; Implements becomes an import.
  • Calls: call_statement, call_expression and raise_event_statement. y = Foo(1) cannot be told apart from array indexing in VB6 syntax, so an unresolved callee simply produces no edge.
  • Class modules (.cls/.frm/.ctl/.dsr/.pag): one Class per file, named from Attribute VB_Name = "..." (falling back to the file stem), with base classes from the designer block type and every Implements. Procedures become Methods with DEFINES_METHOD edges; .bas standard modules keep Functions.
  • Call attribution reuses the existing scope-frame walker: the seeding is one line in cbm_extract_unified(), and both the definition and call sides derive the class QN from a single helper (cbm_vb6_file_class_qn), so they cannot drift. The class name comes from Attribute VB_Name, not from the File node QN, so the File-QN change in v0.11.0 does not affect it.

Open design points

  1. Property Get/Let/Set triplets share one QN and collapse to one Method node (consistent on both sides).
  2. Declare ... Lib and Event declarations inside a .cls become Methods of that class.
  3. Module-level Dim/Const in a .cls stay module-level Variables rather than class Fields (possible follow-up).

Not in this PR

.vbs (no header markers), .vbp/.vbg project files, VB.NET (#1073).

Known grammar gaps

Lines are reported as parse_partial and the files still index. Measured when I first wrote this, not re-measured on the rebase: a call with a leading omitted argument (.Add , x), Print #f, ..., graphics Line (x, y)-(x2, y2), _-continued comments, and the .frm header Object = "{GUID}..." lines.

What a new grammar touches here

Enum, lang_specs.c row, language table, userconfig.c names (vb6, vba, vb, visualbasic), tests, the repro registries, the seven language-count surfaces (162 to 163) with tests/test_language_count_contract.sh, MANIFEST.md (row, ABI tally regenerated from the tree: 9/78/76, re-vendor note), THIRD_PARTY.md, scripts/vendored-checksums.txt, scripts/new-languages.json.

Checklist

  • Every commit is signed off (git commit -s)
  • Tests pass locally. Windows, zig/clang, without ASan/UBSan (no sanitiser runtime on this host), so CI's sanitised runs are the authoritative check:
    • focused suites (language grammar_regression grammar_labels lang_contract extraction): 709 passed, 0 failed, including contract_vb6_class_module_calls_source_from_method
    • full test-runner: 8542 passed, 6 failed, 88 skipped. The same 6 fail on an unmodified main build on this machine (4 in test_cli.c, install/update ACL errors; 2 in test_test_impact_git.c), so they are not caused by this change. The incremental suite was skipped (fixture clone needs network)
    • bash tests/test_language_count_contract.sh: 163 grammars, 7 surfaces
    • all 1088 entries in scripts/vendored-checksums.txt match the committed blobs
  • Lint: clang-format-20 --dry-run --Werror clean on every touched src/ and internal/cbm source and header file; cppcheck not run locally, relying on CI
  • New behaviour is covered by tests; all fixtures are synthetic

Tokarzewski and others added 4 commits October 8, 2026 12:40
Vendors tree-sitter-vba @ 63b2f8d0d65c (v0.13.0, MIT, ABI 15, lexer-only,
no external scanner) as CBM_LANG_VB6 and wires it end to end:

- discover: .bas/.ctl/.dsr/.pag map directly; .cls (Apex/ObjectScript)
  and .frm (FORM) keep their table owners and the DeusData#2074 content sniffers
  now route VB6 modules to CBM_LANG_VB6 instead of CBM_LANG_COUNT.
- lang_specs: Sub/Function/Property Get|Let|Set/Declare/Event as
  callables; Type -> Class with typed Fields (As clause unwrapped);
  Enum -> Enum with members; module-level Dim/Const -> one Variable per
  declarator; call_statement/call_expression/raise_event_statement
  callees; Implements as import; VB6 keyword set; source_file module
  parents.
- release surfaces: MANIFEST (163 grammars, 75x ABI-15, vba row + note
  on the locally generated parser.c), THIRD_PARTY, vendored checksums,
  new-languages.json, and the 162 -> 163 language-count claims on all
  seven contract surfaces.
- tests: extension/sniff mapping, grammar regression + label golden,
  calls contract, extraction block, and the four repro registries
  (capability ledger, call-node manifest, invariant breadth, argument
  matrix B).

Class-module synthesis (.cls/.frm as a Class with Methods) is a
separate follow-up change.

Refs DeusData#721.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Tokarzewski <bartlomiej.tokarzewski@gmail.com>
The classify content-rule table added upstream still expected the
CBM_LANG_COUNT stub for VB6 .cls/.frm; the grammar now routes them.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Signed-off-by: Tokarzewski <bartlomiej.tokarzewski@gmail.com>
A VB6 .cls/.frm/.ctl/.dsr/.pag file is one COM class, but tree-sitter-vba
has no class node, so the file's procedures surfaced as free Functions and
the calls inside them were attributed to the Module.

- helpers.c: cbm_vb6_is_class_module_path / cbm_vb6_file_class_name /
  cbm_vb6_file_class_qn resolve the class from `Attribute VB_Name` (file
  stem fallback). One helper feeds both extractors so the QNs cannot drift;
  it returns NULL for .bas standard modules and for every other language.
- extract_defs.c: cbm_extract_definitions synthesises the Class def (bases
  from the designer `Begin VB.Form` type and every `Implements`) and runs
  the def walk with enclosing_class_qn set to it; extract_func_def promotes
  the file's procedures to Methods (QN class.name, parent_class set so
  DEFINES_METHOD links).
- extract_unified.c: the scope-frame walker's baseline enclosing_class_qn
  starts as the file-class QN instead of NULL. push_scope saves and
  pop_expired_scopes restores that baseline exactly like any other frame
  value, so compute_func_qn yields class.name for every procedure and each
  in-body call sources to the Method, never the Module. No extra WalkState
  field is needed.

Stacks on the VB6 grammar commit that introduces CBM_LANG_VB6.

Refs DeusData#721.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Tokarzewski <bartlomiej.tokarzewski@gmail.com>
- grammar_regression: Widget.cls yields exactly one Class (base IShape),
  two Methods whose parent_class is the class QN, and no Function; the call
  inside Go carries enclosing_func_qn class.Go. ModA.bas yields no Class
  and no Method.
- lang_contract: a Widget.cls CALL_CASES row, plus a dedicated contract
  that indexes the file through the production pipeline and checks the
  CALLS edge into Helper is sourced by the Method Go and by nothing else
  (a Module-sourced edge would surface the module's name as the caller).
- extraction: vb6_implements_is_an_import (from the grammar commit) now
  expects Go in Widget.cls to be a Method, not a Function -- the class
  synthesis this series adds makes every .cls procedure a Method.

Refs DeusData#721.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Tokarzewski <bartlomiej.tokarzewski@gmail.com>
@Tokarzewski
Tokarzewski requested a review from DeusData as a code owner October 8, 2026 10:41
@github-actions

github-actions Bot commented Oct 8, 2026

Copy link
Copy Markdown

Thanks for opening this — it has been seen, and it is queued.

This note is automated, but it is not a brush-off: it exists so you know where your PR stands instead of having to guess from silence.

Current review status: working through a backlog. 0.9.1-rc.1 is out, so the release freeze that held reviews is over — but it left a large queue of open pull requests behind it, and we are reading through them oldest-first. The background is in discussion #1144.

What that means for this PR, concretely:

  • It will not be closed for inactivity. No stale bot touches pull requests here.
  • It may still sit a while before a human reads it. That is on us, not on you.
  • Older PRs are read first, so a recent one is not being skipped — it is behind a queue.

Things that will genuinely speed it up whenever review does happen:

  • Keep it rebased on main — the tree is moving quickly right now, and a conflicting branch cannot be reviewed as the diff you intended.
  • Get CI green, or say which failures you believe are pre-existing.
  • Keep the change to one claim. Bundled features and refactors get split before they get merged, which costs you a round trip.
  • Every commit needs a sign-off (git commit -s) — CI enforces DCO.

If this fixes a bug, a reproduction we can run is worth more than a description of the symptom.

Thanks for contributing, and sorry in advance for the wait.

No code change. test-windows-guards failed on
tests/windows/test_daemon_stability.py (section_start_status_port), a
Windows daemon-port test this branch does not touch; re-triggering CI.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Signed-off-by: Tokarzewski <bartlomiej.tokarzewski@gmail.com>

This branch has not been deployed

No deployments
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