gdscript_api/gdscript_layer.rs
1//! The hand-authored GDScript layer the engine dump omits (Playbook §4.4).
2//!
3//! `extension_api.json` describes the engine (classes, builtins, `@GlobalScope` utilities) but
4//! not the *language* surface GDScript adds on top: the `@GlobalScope`/`@GDScript`
5//! pseudo-constants (`PI`/`TAU`/`INF`/`NAN`) and the GDScript builtin functions
6//! (`preload`/`load`/`range`/`len`/…), whose return types are decided here rather than read
7//! from any dump. `gdscript-hir` consults these during global name resolution.
8//!
9//! Types are tagged with the coarse, model-independent [`LayerTy`] (resolved to a `gdscript-hir`
10//! `Ty` by the consumer) because real [`crate::BuiltinId`]s only exist after the model loads.
11
12/// A coarse type tag for hand-authored symbols, mapped to a `gdscript-hir` `Ty` by the consumer.
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
14pub enum LayerTy {
15 /// `float`.
16 Float,
17 /// `int`.
18 Int,
19 /// `bool`.
20 Bool,
21 /// `String`.
22 Str,
23 /// Bare `Array` (`Array[Variant]`).
24 Array,
25 /// The dynamic `Variant` top type.
26 Variant,
27 /// The Phase-3 seam marker — distinct from `Variant`, never warns (e.g. `preload`).
28 Unknown,
29 /// `void`.
30 Void,
31}
32
33/// A `@GlobalScope`/`@GDScript` pseudo-constant (`PI`, `TAU`, `INF`, `NAN`).
34#[derive(Debug, Clone)]
35pub struct GlobalConst {
36 /// The constant name.
37 pub name: &'static str,
38 /// Its type.
39 pub ty: LayerTy,
40}
41
42/// A GDScript builtin function (`preload`, `range`, `len`, …) — distinct from the
43/// `@GlobalScope` *utility* functions, which come from the JSON.
44#[derive(Debug, Clone)]
45pub struct BuiltinFn {
46 /// The function name.
47 pub name: &'static str,
48 /// Minimum argument count.
49 pub min_args: u8,
50 /// Maximum argument count, or `None` for variadic.
51 pub max_args: Option<u8>,
52 /// The decided return type. `preload`/`load` are refined by `gdscript-hir` per the
53 /// literal-vs-variable argument rule (Playbook §4.4); this is the conservative default.
54 pub ret: LayerTy,
55}
56
57/// The pseudo-constants `extension_api.json` reports as empty `global_constants` in 4.5.
58#[must_use]
59pub fn global_consts() -> Vec<GlobalConst> {
60 use LayerTy::Float;
61 vec![
62 GlobalConst {
63 name: "PI",
64 ty: Float,
65 },
66 GlobalConst {
67 name: "TAU",
68 ty: Float,
69 },
70 GlobalConst {
71 name: "INF",
72 ty: Float,
73 },
74 GlobalConst {
75 name: "NAN",
76 ty: Float,
77 },
78 ]
79}
80
81/// The GDScript builtin functions (the `@GDScript` surface). The list grows as features need
82/// it; these are the ones inference and completion rely on in Phase 2.
83#[must_use]
84pub fn builtin_fns() -> Vec<BuiltinFn> {
85 use LayerTy::{Array, Int, Str, Unknown, Void};
86 vec![
87 // `preload(path)` resolves to a script/resource — opaque in Phase 2 (the seam).
88 BuiltinFn {
89 name: "preload",
90 min_args: 1,
91 max_args: Some(1),
92 ret: Unknown,
93 },
94 // `load(path)` returns a `Resource` at runtime, but the concrete script/resource type is
95 // unknowable statically (the arg may be a variable, and even a literal is a *runtime*
96 // call — NOT a compile-time constant like `preload`). Model it as the seam (`Unknown`) so
97 // `var r := load(...)` neither warns (`INFERENCE_ON_VARIANT`) nor cascades, and `load` is
98 // never aliased to `preload` (Playbook §3.M3 / D5 — both literal and variable args opaque).
99 BuiltinFn {
100 name: "load",
101 min_args: 1,
102 max_args: Some(1),
103 ret: Unknown,
104 },
105 BuiltinFn {
106 name: "range",
107 min_args: 1,
108 max_args: Some(3),
109 ret: Array,
110 },
111 BuiltinFn {
112 name: "len",
113 min_args: 1,
114 max_args: Some(1),
115 ret: Int,
116 },
117 BuiltinFn {
118 name: "char",
119 min_args: 1,
120 max_args: Some(1),
121 ret: Str,
122 },
123 BuiltinFn {
124 name: "assert",
125 min_args: 1,
126 max_args: Some(2),
127 ret: Void,
128 },
129 ]
130}