1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
package shader
import "core:fmt"
// Resolved type representations (used after semantic analysis)
Scalar_Kind :: enum {
Bool,
Int,
Uint,
Float,
Half,
}
Resolved_Type :: union {
Type_Scalar,
Type_Vector,
Type_Matrix,
Type_Struct_Resolved,
Type_Array_Resolved,
Type_Sampler,
Type_Void,
}
Type_Scalar :: struct {
kind: Scalar_Kind,
}
Type_Vector :: struct {
elem: Scalar_Kind,
size: int, // 2, 3, or 4
}
Type_Matrix :: struct {
elem: Scalar_Kind,
cols: int,
rows: int,
}
Type_Struct_Resolved :: struct {
name: string,
fields: []Resolved_Struct_Field,
}
Resolved_Struct_Field :: struct {
name: string,
type: ^Resolved_Type,
attributes: []Ast_Attribute,
}
Type_Array_Resolved :: struct {
elem: ^Resolved_Type,
size: int, // 0 for runtime-sized
}
Type_Sampler :: struct {
kind: Sampler_Kind,
}
Sampler_Kind :: enum {
Sampler2D,
Sampler3D,
SamplerCube,
Sampler2DArray,
Sampler2DShadow,
}
Type_Void :: struct {}
// Type helpers
type_equals :: proc(a, b: ^Resolved_Type) -> bool {
if a == nil && b == nil do return true
if a == nil || b == nil do return false
a_val := a^
b_val := b^
switch av in a_val {
case Type_Scalar:
bv, ok := b_val.(Type_Scalar)
return ok && av.kind == bv.kind
case Type_Vector:
bv, ok := b_val.(Type_Vector)
return ok && av.elem == bv.elem && av.size == bv.size
case Type_Matrix:
bv, ok := b_val.(Type_Matrix)
return ok && av.elem == bv.elem && av.cols == bv.cols && av.rows == bv.rows
case Type_Struct_Resolved:
bv, ok := b_val.(Type_Struct_Resolved)
return ok && av.name == bv.name
case Type_Array_Resolved:
bv, ok := b_val.(Type_Array_Resolved)
return ok && av.size == bv.size && type_equals(av.elem, bv.elem)
case Type_Sampler:
bv, ok := b_val.(Type_Sampler)
return ok && av.kind == bv.kind
case Type_Void:
_, ok := b_val.(Type_Void)
return ok
}
return false
}
type_to_string :: proc(t: ^Resolved_Type, allocator := context.allocator) -> string {
if t == nil do return "void"
switch v in t^ {
case Type_Scalar:
switch v.kind {
case .Bool: return "bool"
case .Int: return "int"
case .Uint: return "uint"
case .Float: return "float"
case .Half: return "half"
}
case Type_Vector:
prefix: string
switch v.elem {
case .Float: prefix = "vec"
case .Int: prefix = "ivec"
case .Uint: prefix = "uvec"
case .Bool: prefix = "bvec"
case .Half: prefix = "hvec"
}
return fmt.aprintf("%s%d", prefix, v.size, allocator = allocator)
case Type_Matrix:
if v.cols == v.rows {
return fmt.aprintf("mat%d", v.cols, allocator = allocator)
}
return fmt.aprintf("mat%dx%d", v.cols, v.rows, allocator = allocator)
case Type_Struct_Resolved:
return v.name
case Type_Array_Resolved:
elem_str := type_to_string(v.elem, allocator)
if v.size == 0 {
return fmt.aprintf("[]%s", elem_str, allocator = allocator)
}
return fmt.aprintf("[%d]%s", v.size, elem_str, allocator = allocator)
case Type_Sampler:
switch v.kind {
case .Sampler2D: return "sampler2D"
case .Sampler3D: return "sampler3D"
case .SamplerCube: return "samplerCube"
case .Sampler2DArray: return "sampler2DArray"
case .Sampler2DShadow: return "sampler2DShadow"
}
case Type_Void:
return "void"
}
return "unknown"
}
is_scalar :: proc(t: ^Resolved_Type) -> bool {
if t == nil do return false
_, ok := t^.(Type_Scalar)
return ok
}
is_vector :: proc(t: ^Resolved_Type) -> bool {
if t == nil do return false
_, ok := t^.(Type_Vector)
return ok
}
is_matrix :: proc(t: ^Resolved_Type) -> bool {
if t == nil do return false
_, ok := t^.(Type_Matrix)
return ok
}
is_numeric_scalar :: proc(t: ^Resolved_Type) -> bool {
if t == nil do return false
s, ok := t^.(Type_Scalar)
return ok && (s.kind == .Float || s.kind == .Int || s.kind == .Uint || s.kind == .Half)
}
is_float_scalar :: proc(t: ^Resolved_Type) -> bool {
if t == nil do return false
s, ok := t^.(Type_Scalar)
return ok && (s.kind == .Float || s.kind == .Half)
}
is_uint_scalar :: proc(t: ^Resolved_Type) -> bool {
if t == nil do return false
s, ok := t^.(Type_Scalar)
return ok && s.kind == .Uint
}
is_float_type :: proc(t: ^Resolved_Type) -> bool {
if t == nil do return false
#partial switch v in t^ {
case Type_Scalar: return v.kind == .Float || v.kind == .Half
case Type_Vector: return v.elem == .Float || v.elem == .Half
case Type_Matrix: return v.elem == .Float || v.elem == .Half
case: return false
}
}
vector_elem :: proc(t: ^Resolved_Type) -> (Scalar_Kind, int, bool) {
if t == nil do return .Float, 0, false
v, ok := t^.(Type_Vector)
if !ok do return .Float, 0, false
return v.elem, v.size, true
}
// Pre-built common types — heap-allocated singletons
make_type :: proc(t: Resolved_Type, allocator := context.allocator) -> ^Resolved_Type {
ptr := new(Resolved_Type, allocator)
ptr^ = t
return ptr
}
// Global type pointers — initialized by @(init)
TYPE_VOID: ^Resolved_Type
TYPE_BOOL: ^Resolved_Type
TYPE_INT: ^Resolved_Type
TYPE_UINT: ^Resolved_Type
TYPE_FLOAT: ^Resolved_Type
TYPE_HALF: ^Resolved_Type
TYPE_VEC2: ^Resolved_Type
TYPE_VEC3: ^Resolved_Type
TYPE_VEC4: ^Resolved_Type
TYPE_IVEC2: ^Resolved_Type
TYPE_IVEC3: ^Resolved_Type
TYPE_IVEC4: ^Resolved_Type
TYPE_UVEC2: ^Resolved_Type
TYPE_UVEC3: ^Resolved_Type
TYPE_UVEC4: ^Resolved_Type
TYPE_BVEC2: ^Resolved_Type
TYPE_BVEC3: ^Resolved_Type
TYPE_BVEC4: ^Resolved_Type
TYPE_MAT2: ^Resolved_Type
TYPE_MAT3: ^Resolved_Type
TYPE_MAT4: ^Resolved_Type
TYPE_SAMPLER2D: ^Resolved_Type
TYPE_SAMPLER2D_SHADOW: ^Resolved_Type
_types_initialized := false
init_builtin_types :: proc() {
_types_initialized = true
TYPE_VOID = make_type(Type_Void{})
TYPE_BOOL = make_type(Type_Scalar{.Bool})
TYPE_INT = make_type(Type_Scalar{.Int})
TYPE_UINT = make_type(Type_Scalar{.Uint})
TYPE_FLOAT = make_type(Type_Scalar{.Float})
TYPE_HALF = make_type(Type_Scalar{.Half})
TYPE_VEC2 = make_type(Type_Vector{.Float, 2})
TYPE_VEC3 = make_type(Type_Vector{.Float, 3})
TYPE_VEC4 = make_type(Type_Vector{.Float, 4})
TYPE_IVEC2 = make_type(Type_Vector{.Int, 2})
TYPE_IVEC3 = make_type(Type_Vector{.Int, 3})
TYPE_IVEC4 = make_type(Type_Vector{.Int, 4})
TYPE_UVEC2 = make_type(Type_Vector{.Uint, 2})
TYPE_UVEC3 = make_type(Type_Vector{.Uint, 3})
TYPE_UVEC4 = make_type(Type_Vector{.Uint, 4})
TYPE_BVEC2 = make_type(Type_Vector{.Bool, 2})
TYPE_BVEC3 = make_type(Type_Vector{.Bool, 3})
TYPE_BVEC4 = make_type(Type_Vector{.Bool, 4})
TYPE_MAT2 = make_type(Type_Matrix{.Float, 2, 2})
TYPE_MAT3 = make_type(Type_Matrix{.Float, 3, 3})
TYPE_MAT4 = make_type(Type_Matrix{.Float, 4, 4})
TYPE_SAMPLER2D = make_type(Type_Sampler{.Sampler2D})
TYPE_SAMPLER2D_SHADOW = make_type(Type_Sampler{.Sampler2DShadow})
}