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}) }