本文档介绍了 GDScript 的实现,GDScript 是 Godot Engine 内置的脚本语言。它解释了 GDScript 代码如何在引擎中被解析、分析、编译和执行。有关使用 GDScript 作为语言的信息,请参阅 GDScript 基础。
GDScript 是一种动态类型的、类似 Python 的语言,专为 Godot Engine 设计。其实现主要包括四个阶段:
下图说明了整个流程:
来源: modules/gdscript/gdscript_parser.cpp modules/gdscript/gdscript_analyzer.cpp modules/gdscript/gdscript_compiler.cpp modules/gdscript/gdscript.cpp modules/gdscript/gdscript_function.cpp
GDScript 的实现包含几个协同工作的关键类:
来源: modules/gdscript/gdscript.h60-172 modules/gdscript/gdscript_tokenizer.h modules/gdscript/gdscript_parser.h54-140 modules/gdscript/gdscript_analyzer.h39-62 modules/gdscript/gdscript_compiler.h modules/gdscript/gdscript_function.h
GDScript 实现的第一阶段是解析,它将源代码转换为抽象语法树 (AST)。
在解析之前,源代码由 GDScriptTokenizer 分解为标记。分词器识别语言元素,如关键字、标识符、字面量和运算符。
分词器处理 GDScriptTokenizer::Token::Type 枚举中定义的各种标记类型,包括:
if、else、func、class 等)+、-、*、/ 等)(、)、{、} 等)来源: modules/gdscript/gdscript_tokenizer.cpp44-162 modules/gdscript/gdscript_tokenizer.h58-164
解析器 (GDScriptParser) 按顺序处理标记,以构建表示代码结构的 AST。AST 中的每个节点代表一个不同的语言构造。
主要的节点类型包括:
ClassNode:代表一个 GDScript 类,包含成员声明。FunctionNode:代表函数声明。VariableNode:代表变量声明。ExpressionNode:所有表达式(字面量、运算等)的基类。SuiteNode:代表一个语句序列。解析器通过使用预测解析方法递归处理标记流来构建此树。它从入口点 parse_program() 开始,该函数识别脚本的顶层结构。
来源: modules/gdscript/gdscript_parser.cpp608-771 modules/gdscript/gdscript_parser.h95-367 modules/gdscript/gdscript_analyzer.cpp85-131 modules/gdscript/gdscript_analyzer.cpp632-733
在解析之后,GDScriptAnalyzer 对 AST 进行语义分析。这包括:
GDScript 同时支持动态类型和静态类型。分析器使用 GDScriptParser::DataType 结构来跟踪类型,该结构包含有关以下方面的信息:
使用静态类型时,分析器会验证赋值、函数参数和返回值的类型兼容性。
来源: modules/gdscript/gdscript_parser.h101-186 modules/gdscript/gdscript_analyzer.cpp85-131 modules/gdscript/gdscript_analyzer.cpp632-733
GDScript 支持类继承,这在分析阶段进行解析。分析器会:
extends 中指定的基类。此过程确保了正确的类型检查和成员解析,保证子类可以访问父类的成员。
来源: modules/gdscript/gdscript_analyzer.cpp344-632 modules/gdscript/gdscript.cpp396-633
分析器会验证类成员(变量、函数、信号等)是否定义正确。
它还会验证注解(如 @export、@onready 等),这些注解为成员提供了额外的信息或行为。
来源: modules/gdscript/gdscript_analyzer.cpp232-267 modules/gdscript/gdscript_analyzer.h220-263
代码完全分析后,GDScriptCompiler 会将其编译成可由 GDScript 虚拟机执行的字节码。
GDScriptByteCodeGenerator 将分析后的 AST 转换为一系列代表程序操作的字节码指令。
每个字节码指令都包含一个操作码(opcode)和可选的操作数。例如,OPERATOR 指令对值执行操作,而 CALL 指令则调用一个函数。
来源: modules/gdscript/gdscript_byte_codegen.cpp37-169 modules/gdscript/gdscript_byte_codegen.h42-116
函数被编译成 GDScriptFunction 对象,每个对象包含:
编译器处理不同类型的函数,包括常规函数、静态函数和 lambda 函数。
来源: modules/gdscript/gdscript_compiler.cpp1101-1398 modules/gdscript/gdscript_function.h44-105
编译器构建一个完整的 GDScript 对象,包含:
此脚本对象成为 GDScript 类的运行时表示,并在使用脚本实例化对象时使用。
来源: modules/gdscript/gdscript_compiler.cpp2101-2231 modules/gdscript/gdscript.h97-246
最后一个阶段是由 GDScript 虚拟机执行编译后的字节码。
GDScript VM 是一个基于堆栈的虚拟机,主要在 gdscript_function.cpp 和 gdscript_vm.cpp 中实现。它执行编译过程中生成的字节码指令。
VM 按顺序处理字节码指令,维护:
对于每条指令,VM 执行相应的操作并相应地更新其状态。
来源: modules/gdscript/gdscript_vm.cpp117-1431 modules/gdscript/gdscript_function.cpp35-135
实例化 GDScript 类时:
GDScriptInstance,并连接到拥有者对象。_init)会被调用。_ready)可能会稍后进行。当在 GDScript 实例上调用函数时,VM 会执行相应的字节码来执行函数的操作。
来源: modules/gdscript/gdscript.cpp151-250 modules/gdscript/gdscript_function.cpp137-206
GDScript 实现包括全面的错误处理:
当发生错误时,会报告具体信息,例如错误位置(文件、行、列)和描述性消息。
在调试模式下,可以使用断点、调用堆栈检查和变量检查等附加功能来辅助调试。
来源: modules/gdscript/gdscript_vm.cpp153-222 modules/gdscript/gdscript_parser.cpp179-214 modules/gdscript/gdscript_editor.cpp242-380
GDScript 支持可选的静态类型,允许开发者为变量、函数参数和返回值指定类型。
类型系统包括:
静态类型可以实现更好的代码验证、自动补全,并可能提高性能。
来源: modules/gdscript/gdscript_analyzer.cpp632-770 modules/gdscript/gdscript_compiler.cpp89-207
GDScript 实现了一个基于类的面向对象模型。
类支持:
extends 实现的单继承。_init)该实现确保了正确的成员解析,包括从基类继承。
来源: modules/gdscript/gdscript.h60-246 modules/gdscript/gdscript.cpp123-246 modules/gdscript/gdscript_analyzer.cpp344-632
GDScript 与 Godot 的标准库集成,提供对以下内容的访问:
print、range 等)集成是通过以下方式实现的:
gdscript_utility_functions.cpp 中的实用函数来源: modules/gdscript/gdscript_utility_functions.cpp136-602 modules/gdscript/gdscript_editor.cpp457-518
GDScript 是一个完全集成、专门为 Godot 引擎量身定制的脚本语言。其实现遵循标准的编译器流程,包括解析、分析、编译和执行,并针对游戏开发的需求进行了特殊调整。
该语言在易用性和强大的功能之间取得了平衡,例如可选的静态类型、全面的面向对象支持以及与引擎的紧密集成。这使得它成为使用 Godot 开发游戏的有效工具,既能满足初学者的可访问性,又能满足高级用户的需求。