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.- AnalyzerTokenizer
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),
extendstargets, andclass_nameregistrations. - 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,@onreadyplacements. - 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
Variantregisters per function frame. - Native call dispatch through
MethodBind. OPCODE_AWAITintegrates withSignalsoawait something.signalresumes 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
GDScriptis registered withScriptServerfromregister_types.cppso 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
EngineDebuggerplus GDScript-specific frame inspection ingdscript.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.cppplus a doc entry indoc_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.