Skip to content

feat(extraction): add GDScript language support - #1579

Open
Plumvery wants to merge 1 commit into
colbymchenry:mainfrom
Plumvery:feat/gdscript-language-support
Open

feat(extraction): add GDScript language support#1579
Plumvery wants to merge 1 commit into
colbymchenry:mainfrom
Plumvery:feat/gdscript-language-support

Conversation

@Plumvery

Copy link
Copy Markdown

Summary

Adds GDScript (.gd, Godot Engine) as a supported language on the wasm extraction path, following the shape of previous language additions (Nix #1190, ArkTS #648).

  • Extractor (src/extraction/languages/gdscript.ts, ~100 lines): functions with typed signatures, _init constructors, inner classes with methods, the full var/const/@export/@onready variable family, signals, enums with members, static func detection, and call edges through call / attribute_call / base_call.
  • Grammar: PrestonKnopp/tree-sitter-gdscript v6.1.0 (MIT), vendored as an ABI-15 wasm rebuilt from upstream source (tree-sitter-cli 0.25.10 generate + build --wasm, emscripten/emsdk:4.0.4), external scanner (indentation) included — the same approach as the Nix wasm.

Grammar shapes that needed care

  • The var/const family names its target via a name-typed child, not identifier, so the core's generic variable fallback can't read them. A visitNode hook creates variable/constant nodes itself, then walks initializers so calls inside them (preload(...), Foo.new()) are captured.
  • func _init(...) (constructor_definition) has no name fieldresolveName supplies the conventional _init. enumerator names its identifier via left.
  • static on functions is a named static_keyword child with no field (the var statements carry it via a field) — a child scan covers both.
  • signal foo(a, b) extracts as a property carrying the parameter list, so connect()-heavy scripts expose their signal surface in the graph.
  • obj.method(args) parses as attribute(identifier, attribute_call(...))attribute_call joins callTypes and the core's namedChild(0) callee fallback yields the bare method name (name-match resolution, same as self/this receivers elsewhere).

Validation

scripts/add-lang/check-grammar.mjs: ABI 15, 20/20 clean parses, heap-safe under multi-grammar reuse (the Lua ABI-13 corruption scenario this script exists for does not occur).

scripts/add-lang/verify-extraction.mjs criteria on two corpora (all critical and soft checks pass):

Corpus .gd files nodes edges index time
godotengine/godot-demo-projects (public) 461 5,216 6,783 3.7s
Private Godot 4 game 138 2,996 11,484 1.8s

Kind spread on the public corpus: 2,027 functions / 184 methods / 45 classes / 141 signals→properties / 39 enums + 130 members / 1,570 variables / 422 constants — symbol density well above the 1-per-file soft bar.

Tests: 4 new extraction describe-blocks + detection/support assertions in __tests__/extraction.test.ts; full vitest suite passes locally (all pre-existing tests unaffected).

Not included (deliberately small)

  • No kernel routing (stays on the wasm path, like the other recent language additions).
  • No preload/load import-edge resolution to res:// paths (calls are captured; resolving them to file nodes would follow the Luau instance-path require pattern and can come as a follow-up).
  • No README language-grid SVG icon (asset pipeline is maintainer-side).

🤖 Generated with Claude Code

Adds GDScript (.gd) as a supported language on the wasm extraction path:

- Extractor (src/extraction/languages/gdscript.ts): functions and typed
  signatures, `_init` constructors (no name field in the grammar —
  resolveName supplies the conventional name), inner classes with methods,
  enums with members (`enumerator` names via `left`), `static func`
  detection (static_keyword child scan), and call edges through `call`,
  `attribute_call`, and `base_call`.
- The var/const family (`variable_statement`, `export_variable_statement`,
  `onready_variable_statement`, `const_statement`) names its target via a
  `name`-typed child, not `identifier`, so the core's generic variable
  fallback can't read them — a visitNode hook creates variable/constant
  nodes itself and walks initializers so calls inside them (`preload(...)`,
  `Foo.new()`) are captured.
- Signals extract as properties carrying their parameter list, so
  connect()-heavy scripts expose their signal surface in the graph.
- Grammar: PrestonKnopp/tree-sitter-gdscript v6.1.0 (MIT), vendored as an
  ABI-15 wasm rebuilt from upstream source (tree-sitter-cli 0.25.10
  generate + build --wasm, emscripten/emsdk:4.0.4); external scanner
  (indentation) included. check-grammar.mjs: ABI 15, 20/20 clean parses,
  heap-safe under multi-grammar reuse.

Validated with the add-lang verification gate on two corpora:
- godotengine/godot-demo-projects (public): 461 .gd files -> 5,216 nodes /
  6,783 edges in 3.7s (classes 45, methods 184, signals 141, enums 39+130).
- A private Godot 4 game (138 .gd files): 2,996 nodes / 11,484 edges;
  all critical and soft checks pass (2,858 structural symbols, density
  20.7/file).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
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