菜单

GDScript 实现

相关源文件

本文档介绍了 GDScript 的实现,GDScript 是 Godot Engine 内置的脚本语言。它解释了 GDScript 代码如何在引擎中被解析、分析、编译和执行。有关使用 GDScript 作为语言的信息,请参阅 GDScript 基础

概述

GDScript 是一种动态类型的、类似 Python 的语言,专为 Godot Engine 设计。其实现主要包括四个阶段:

  1. 解析:将源代码文本转换为抽象语法树 (AST)。
  2. 分析:类型检查、验证 AST 和解析引用。
  3. 编译:从分析后的 AST 生成字节码。
  4. 执行:在 GDScript 虚拟机中运行字节码。

下图说明了整个流程:

来源: 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 枚举中定义的各种标记类型,包括:

  • 关键字(ifelsefuncclass 等)
  • 运算符(+-*/ 等)
  • 字面量(数字、字符串)
  • 标识符(变量/函数名)
  • 特殊符号((){} 等)

来源: modules/gdscript/gdscript_tokenizer.cpp44-162 modules/gdscript/gdscript_tokenizer.h58-164

抽象语法树 (AST) 构建

解析器 (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 进行语义分析。这包括:

  1. 类型检查
  2. 解析类继承关系。
  3. 验证表达式和语句。
  4. 检测错误和警告。

类型系统

GDScript 同时支持动态类型和静态类型。分析器使用 GDScriptParser::DataType 结构来跟踪类型,该结构包含有关以下方面的信息:

  • 类型的种类(内置、原生、脚本、类、枚举)。
  • 类型来源(显式注解、推断、未检测)。
  • 类型特定信息(内置类型、原生类、脚本引用)。

使用静态类型时,分析器会验证赋值、函数参数和返回值的类型兼容性。

来源: modules/gdscript/gdscript_parser.h101-186 modules/gdscript/gdscript_analyzer.cpp85-131 modules/gdscript/gdscript_analyzer.cpp632-733

类继承解析

GDScript 支持类继承,这在分析阶段进行解析。分析器会:

  1. 解析 extends 中指定的基类。
  2. 验证继承是否有效(例如,无循环继承)。
  3. 合并父类的成员信息。

此过程确保了正确的类型检查和成员解析,保证子类可以访问父类的成员。

来源: modules/gdscript/gdscript_analyzer.cpp344-632 modules/gdscript/gdscript.cpp396-633

成员验证

分析器会验证类成员(变量、函数、信号等)是否定义正确。

  1. 检查与基类成员的名称冲突。
  2. 验证函数签名。
  3. 检查信号声明。
  4. 验证变量类型和默认值。

它还会验证注解(如 @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 对象,每个对象包含:

  1. 函数体的字节码
  2. 局部变量信息
  3. 参数信息
  4. 函数中使用的常量
  5. 调试信息(行号等)

编译器处理不同类型的函数,包括常规函数、静态函数和 lambda 函数。

来源: modules/gdscript/gdscript_compiler.cpp1101-1398 modules/gdscript/gdscript_function.h44-105

类和脚本生成

编译器构建一个完整的 GDScript 对象,包含:

  1. 已编译的函数
  2. 类成员信息
  3. 常量
  4. 信号
  5. 继承信息

此脚本对象成为 GDScript 类的运行时表示,并在使用脚本实例化对象时使用。

来源: modules/gdscript/gdscript_compiler.cpp2101-2231 modules/gdscript/gdscript.h97-246

运行时执行

最后一个阶段是由 GDScript 虚拟机执行编译后的字节码。

GDScript 虚拟机

GDScript VM 是一个基于堆栈的虚拟机,主要在 gdscript_function.cppgdscript_vm.cpp 中实现。它执行编译过程中生成的字节码指令。

VM 按顺序处理字节码指令,维护:

  • 用于临时值的堆栈
  • 局部变量
  • 类成员和全局变量的引用

对于每条指令,VM 执行相应的操作并相应地更新其状态。

来源: modules/gdscript/gdscript_vm.cpp117-1431 modules/gdscript/gdscript_function.cpp35-135

实例创建与执行

实例化 GDScript 类时:

  1. 会创建一个 GDScriptInstance,并连接到拥有者对象。
  2. 实例的构造函数(_init)会被调用。
  3. 成员变量会被初始化。
  4. 其他初始化(如 _ready)可能会稍后进行。

当在 GDScript 实例上调用函数时,VM 会执行相应的字节码来执行函数的操作。

来源: modules/gdscript/gdscript.cpp151-250 modules/gdscript/gdscript_function.cpp137-206

错误处理

GDScript 实现包括全面的错误处理:

  1. 解析阶段的语法错误
  2. 分析阶段的分析错误
  3. 运行时发生的运行时错误

当发生错误时,会报告具体信息,例如错误位置(文件、行、列)和描述性消息。

在调试模式下,可以使用断点、调用堆栈检查和变量检查等附加功能来辅助调试。

来源: modules/gdscript/gdscript_vm.cpp153-222 modules/gdscript/gdscript_parser.cpp179-214 modules/gdscript/gdscript_editor.cpp242-380

高级特性

静态类型系统

GDScript 支持可选的静态类型,允许开发者为变量、函数参数和返回值指定类型。

类型系统包括:

  • 内置类型(int、float、String 等)
  • 引擎类(Node、Resource 等)
  • 脚本类(GDScript 和其他语言)
  • 枚举
  • 泛型容器(Array[Type]、Dictionary[KeyType, ValueType])

静态类型可以实现更好的代码验证、自动补全,并可能提高性能。

来源: 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 的标准库集成,提供对以下内容的访问:

  • 引擎类和函数
  • 核心类型及其方法
  • 全局函数和常量
  • 实用函数(printrange 等)

集成是通过以下方式实现的:

  1. 直接绑定到引擎类和函数
  2. gdscript_utility_functions.cpp 中的实用函数
  3. 内置常量和全局属性

来源: modules/gdscript/gdscript_utility_functions.cpp136-602 modules/gdscript/gdscript_editor.cpp457-518

结论

GDScript 是一个完全集成、专门为 Godot 引擎量身定制的脚本语言。其实现遵循标准的编译器流程,包括解析、分析、编译和执行,并针对游戏开发的需求进行了特殊调整。

该语言在易用性和强大的功能之间取得了平衡,例如可选的静态类型、全面的面向对象支持以及与引擎的紧密集成。这使得它成为使用 Godot 开发游戏的有效工具,既能满足初学者的可访问性,又能满足高级用户的需求。