Open-Source Wikis

/

Godot

/

Modules

/

GDScript

godotengine/godot

GDScript

Purpose

modules/gdscript/ implements GDScript: Godot's built-in, dynamically-typed, indentation-sensitive scripting language. The module ships a complete pipeline (tokenizer → parser → analyzer → byte-code compiler → virtual machine → debugger), an LSP server, and the editor-side integration (syntax highlighting, completions, navigation, refactors).

Directory layout

modules/gdscript/
├── gdscript.{cpp,h}                The Script subclass (~85 KB cpp, ~26 KB h)
├── gdscript_tokenizer.{cpp,h}      Lexer (~43 KB cpp)
├── gdscript_tokenizer_buffer.{cpp,h}  Binary token cache (the .gdc format)
├── gdscript_parser.{cpp,h}         Recursive-descent parser → AST (~230 KB cpp)
├── gdscript_analyzer.{cpp,h}       Semantic analyzer + type inference (~275 KB cpp)
├── gdscript_compiler.{cpp,h}       AST → bytecode (~132 KB cpp)
├── gdscript_byte_codegen.{cpp,h}   Bytecode emission helpers (~75 KB cpp)
├── gdscript_function.{cpp,h}       Compiled function (instructions + constants + line table)
├── gdscript_vm.cpp                 The interpreter (~131 KB)
├── gdscript_disassembler.cpp       Bytecode pretty-printer (used by tests + tools)
├── gdscript_cache.{cpp,h}          Cross-script reference resolution + compilation cache
├── gdscript_resource_format.{cpp,h}  ResourceFormatLoader/Saver for .gd / .gdc
├── gdscript_lambda_callable.{cpp,h}  Callable that wraps a script lambda
├── gdscript_rpc_callable.{cpp,h}   Callable for @rpc-decorated methods
├── gdscript_utility_callable.{cpp,h}  Callable for built-in utility functions
├── gdscript_utility_functions.{cpp,h}  Implementations of `print`, `range`, `len`, …
├── gdscript_warning.{cpp,h}        Warning system (typed-loops, unused-vars, etc.)
├── gdscript_editor.cpp             Code completion, doctips, navigation (~172 KB)
├── editor/                         GDScript-specific editor integrations (highlight, debugger panel)
├── language_server/                LSP server (used by external editors and the in-engine script editor)
├── doc_classes/                    XML class reference for @GDScript and @GlobalScope
├── icons/                          Editor icons
└── tests/                          doctest unit tests (parser, analyzer, vm, integration)

Pipeline

graph LR
    Source[".gd source"] --> Tokenizer
    Tokenizer -->|tokens| Parser
    Parser -->|AST| Analyzer
    Analyzer -->|typed AST| Compiler
    Compiler -->|bytecode| Function[GDScriptFunction]
    Function -->|run| VM[GDScript VM]
    VM -->|Variant calls| Engine[Object / Variant API]
    Source ---|.gdc binary cache| Tokenizer
    Cache[GDScriptCache] -.deps.- Parser
    Cache -.deps.- Analyzer

Tokenizer

gdscript_tokenizer.cpp produces tokens. INDENT/DEDENT are explicit tokens (Python-style); strings support multi-line and r-strings; numbers support hex, binary, scientific notation. The buffer tokenizer (gdscript_tokenizer_buffer.cpp) reads/writes the .gdc binary cache used for shipping pre-tokenized scripts.

Parser

gdscript_parser.cpp (~230 KB — the largest single C++ file in any module) builds an AST. GDScript syntax includes:

  • func, var, const, signal, enum, class_name, extends
  • Static typing annotations (var x: int = 0, func f(a: Array[int]) -> void).
  • Match statements with destructuring (match value: { ... }).
  • Lambdas (func(): pass), closures, await, signals.
  • @tool, @onready, @export, @export_range, @rpc, @warning_ignore, … annotations.
  • Inner classes (a script can declare named sub-classes that extend Object).

The parser recovers from many errors so the analyzer can still emit useful completions and warnings. Errors are recorded with file/line/column.

Analyzer

gdscript_analyzer.cpp is the semantic pass. It:

  • Resolves identifiers against current/parent scopes, parent classes (inheritance), extends targets, and class_name registrations.
  • Performs type inference. GDScript supports inferred typing (var x := some_call()); the analyzer derives the type from the right-hand side and checks compatibility.
  • Validates @tool, @export, @onready placements.
  • Computes the script's exposed members (properties + signals + methods) for ClassDB-like introspection.
  • Drives the warning system (gdscript_warning.cpp) — warnings cover unreachable code, unused variables, narrowing types, untyped declarations, etc.

Compiler

gdscript_compiler.cpp lowers the typed AST to a byte-code instruction stream. The bytecode is a register-machine targeting Variants; it has dedicated opcodes for typed arithmetic (OPCODE_OPERATOR_ADD_INT, etc.) so typed code skips runtime type checks.

gdscript_byte_codegen.cpp emits the instructions and tracks register reuse (the compiler pre-allocates a fixed number of "stack" registers per function plus per-instruction temporaries).

VM

gdscript_vm.cpp is the interpreter. It is a giant computed-goto-style switch on opcode, with:

  • ~150 distinct opcodes (typed/untyped operators, variant constructors, indexing, calls, await, signals, error handling, lambdas, returns).
  • A stack of Variant registers per function frame.
  • Native call dispatch through MethodBind.
  • OPCODE_AWAIT integrates with Signal so await something.signal resumes when the signal is emitted.

Recursion uses a cooperative scheme — each await yields the function and the resumption is scheduled when the awaited callable resolves.

Cross-script references

GDScriptCache (gdscript_cache.cpp) tracks dependencies between scripts (one extends another, references a class_name, or instantiates a preload). It enforces the right compile order, invalidates downstream caches when an upstream script changes, and avoids re-tokenizing files unnecessarily.

This is what lets you split a project across many .gd files and edit them in any order without manual recompilation steps.

Editor integration

gdscript_editor.cpp (~172 KB) implements:

  • Auto-completion — context-aware: from the current AST + analyzer state, computes possible identifiers, methods, properties, signals.
  • Tooltips / hover info — symbol resolution + formatted summaries from the doc XML.
  • Goto definition — uses the parser's saved positions to navigate to declarations.
  • Find references — analyzer scans cached scripts for usages.
  • Refactoring — rename + extract method are partial today; rename works across files within scope.
  • Live reload — when the user edits a script in the script editor, scripts attached to live nodes can refresh without restarting the game (limited to compatible signature changes).

The LSP server under language_server/ uses the same core: tokenizer + parser + analyzer + the exposed completion API. External editors (VSCode, Vim, Helix) can connect.

Built-in utility functions

gdscript_utility_functions.cpp is a registry of utility functions exposed to GDScript (some shared with Variant utilities, some GDScript-only). Examples: print, print_rich, printerr, range, len, is_instance_valid, is_same, weakref, print_debug. They are dispatched through GDScriptUtilityCallable.

Lambdas and Callables

gdscript_lambda_callable.cpp produces GDScriptLambdaCallable/GDScriptLambdaSelfCallable so a script can build a Callable from func(): print(x) and pass it around. Captured variables become part of the Callable's closure state. RPC-decorated methods get GDScriptRPCCallable so calls go through MultiplayerAPI instead of being invoked locally.

Bytecode disassembly

gdscript_disassembler.cpp prints instructions for a compiled function. OS::set_environment("GODOT_DEBUG_GDSCRIPT_DISASSEMBLER", "1") (or test runs) emit disassembly for inspection.

Key abstractions

Abstraction File Role
GDScript modules/gdscript/gdscript.cpp Script subclass — owns the source, the function table, and the per-instance bookkeeping
GDScriptInstance modules/gdscript/gdscript.cpp Per-Object script state (member values, signal connections)
GDScriptParser modules/gdscript/gdscript_parser.cpp Source → AST
GDScriptAnalyzer modules/gdscript/gdscript_analyzer.cpp Type and symbol resolution
GDScriptCompiler modules/gdscript/gdscript_compiler.cpp AST → bytecode
GDScriptFunction modules/gdscript/gdscript_function.cpp Compiled function (opcodes + constants + line table)
GDScriptCache modules/gdscript/gdscript_cache.cpp Cross-script dependency tracking
GDScriptLanguageProtocol modules/gdscript/language_server/gdscript_language_protocol.cpp LSP server

Integration points

  • GDScript is registered with ScriptServer from register_types.cpp so the engine treats it as a first-class scripting language.
  • The editor's script editor is generic but knows how to delegate completion/diagnostics to GDScript via ScriptLanguage::* virtual methods.
  • The debugger uses EngineDebugger plus GDScript-specific frame inspection in gdscript.cpp.

Entry points for modification

  • New language feature → tokenizer (recognise tokens) → parser (consume + AST shape) → analyzer (typing rules) → compiler (lowering) → VM (opcode if needed). The tests under modules/gdscript/tests/ cover each stage.
  • New built-in utility function → gdscript_utility_functions.cpp plus a doc entry in doc_classes/@GDScript.xml.
  • New warning → declare in gdscript_warning.h, emit from analyzer, add to default warning levels.
  • LSP capability → extend language_server/. The protocol implementation is straightforward JSON-RPC 2.0.

Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.

GDScript – Godot wiki | Factory