// Copyright 2026 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///|
pub(all) struct EvalResult {
value : String
buffer : Bytes?
} derive(Eq, Debug)
///|
pub fn EvalResult::to_string(self : EvalResult) -> String {
self.value
}
///|
pub fn EvalResult::to_buffer(self : EvalResult) -> Bytes? {
self.buffer
}
///|
pub(all) enum UserInputValue {
Bool(Bool)
I32(Int)
U32(Int)
F32(Double)
I32Vector(Array[Int])
U32Vector(Array[Int])
F32Vector(Array[Double])
} derive(Eq, Debug)
///|
pub(all) struct UserInput {
location : Int
value : UserInputValue
} derive(Eq, Debug)
///|
pub(all) struct Inputs {
vertex_index : Int?
instance_index : Int?
position : Array[Double]?
front_facing : Bool?
sample_index : Int?
sample_mask : Int?
local_invocation_id : Array[Int]?
local_invocation_index : Int?
global_invocation_id : Array[Int]?
workgroup_id : Array[Int]?
num_workgroups : Array[Int]?
subgroup_invocation_id : Int?
subgroup_size : Int?
subgroup_id : Int?
num_subgroups : Int?
primitive_index : Int?
view_index : Int?
user_defined : Array[UserInput]
} derive(Eq, Debug)
///|
pub fn Inputs::new_zero_initialized() -> Inputs {
{
vertex_index: Some(0),
instance_index: Some(0),
position: Some([0.0, 0.0, 0.0, 0.0]),
front_facing: Some(true),
sample_index: Some(0),
sample_mask: Some(0),
local_invocation_id: Some([0, 0, 0]),
local_invocation_index: Some(0),
global_invocation_id: Some([0, 0, 0]),
workgroup_id: Some([0, 0, 0]),
num_workgroups: Some([1, 1, 1]),
subgroup_invocation_id: Some(0),
subgroup_size: Some(4),
subgroup_id: Some(0),
num_subgroups: Some(1),
primitive_index: Some(0),
view_index: Some(0),
user_defined: [],
}
}
///|
pub(all) struct ResourceBinding {
group : Int
binding : Int
kind : String
data : Bytes
} derive(Eq, Debug)
///|
pub(all) struct OverrideValue {
name : String
value : String
} derive(Eq, Debug)
///|
pub(all) struct ExecResult {
value : String?
buffer : Bytes?
resources : Array[ResourceBinding]
} derive(Eq, Debug)
///|
pub fn ExecResult::return_value(self : ExecResult) -> String? {
self.value
}
///|
pub fn ExecResult::to_buffer(self : ExecResult) -> Bytes? {
self.buffer
}
///|
pub fn ExecResult::resource(
self : ExecResult,
group : Int,
binding : Int,
) -> ResourceBinding? {
for resource in self.resources {
if resource.group == group && resource.binding == binding {
return Some(resource)
}
}
None
}
///|
pub fn ExecResult::to_string(self : ExecResult) -> String {
match self.value {
Some(value) => "return: \{value}"
None => "return: void"
}
}
///|
priv struct ConstEvalEntryExec {
value : ConstEvalValue?
ctx : ConstEvalContext
}
///|
fn eval_const_function_value(
source : String,
target_expr : String,
) -> ConstEvalValue raise WeslCompileError {
let tokens = const_eval_lex(source)
let parser = ConstEvalParser::new(tokens)
let function = parser.parse_function()
let ctx = ConstEvalContext::new()
let args : Array[ConstEvalValue] = []
for
arg in const_eval_validate_target(
target_expr.trim().to_owned(),
function.name,
) {
args.push(const_eval_eval_expr(arg, ctx))
}
const_eval_execute_function_value(function, args, ctx.new_call_scope())
}
///|
fn const_eval_parse_expression_value(
source : String,
ctx : ConstEvalContext,
) -> ConstEvalValue raise WeslCompileError {
let tokens = const_eval_lex(source)
let parser = ConstEvalParser::new(tokens)
let expr = parser.parse_expression()
if !parser.view().is_empty() {
raise Validation("unexpected trailing const-eval expression tokens")
}
const_eval_eval_expr(expr, ctx)
}
///|
fn eval_const_expression_value(
source : String,
) -> ConstEvalValue raise WeslCompileError {
const_eval_parse_expression_value(source, ConstEvalContext::new())
}
///|
fn const_eval_parse_type_source(
source : String,
) -> ConstEvalType raise WeslCompileError {
let tokens = const_eval_lex(source)
let parser = ConstEvalParser::new(tokens)
let type_ = parser.parse_type()
if !parser.view().is_empty() {
raise Validation("unexpected trailing const-eval type tokens")
}
type_
}
///|
fn const_eval_struct_declaration(
unit : TranslationUnit,
name : String,
) -> StructDeclaration? {
for declaration in unit.global_declarations {
match declaration.header {
Struct(struct_) => if struct_.name == name { return Some(struct_) }
_ => ()
}
}
None
}
///|
fn const_eval_type_source_with_unit(
unit : TranslationUnit,
source : String,
) -> ConstEvalType raise WeslCompileError {
let type_ = const_eval_parse_type_source(source)
match type_ {
Struct(name) =>
match const_eval_struct_declaration(unit, name) {
Some(_) => type_
None => raise Validation("unknown type `\{name}`")
}
_ => type_
}
}
///|
fn const_eval_function_from_source(
source : String,
) -> ConstEvalFunction raise WeslCompileError {
let tokens = const_eval_lex(source)
let parser = ConstEvalParser::new(tokens)
parser.parse_function()
}
///|
fn const_eval_context_from_translation_unit(
unit : TranslationUnit,
) -> ConstEvalContext raise WeslCompileError {
const_eval_context_from_translation_unit_with_exec_inputs(unit, [], [])
}
///|
fn const_eval_override_value(
overrides : Array[OverrideValue],
name : String,
) -> String? {
let mut value : String? = None
for override_ in overrides {
if override_.name == name {
value = Some(override_.value)
}
}
value
}
///|
fn const_eval_attr_i32(attr : Attribute) -> Int? raise WeslCompileError {
match attr.arguments {
Some(text) =>
match const_eval_parse_expression_value(text, ConstEvalContext::new()) {
AbstractInt(value) | I32(value) | U32(value) =>
if value >= 0L && value <= 2147483647L {
Some(value.to_int())
} else {
raise Validation(
"resource attribute `\{attr.name}` is out of range",
)
}
value =>
raise Validation(
"resource attribute `\{attr.name}` must evaluate to an integer, got \{const_eval_render_value(value)}",
)
}
None => None
}
}
///|
fn const_eval_named_attr_i32(
attributes : Array[Attribute],
name : String,
) -> Int? raise WeslCompileError {
let mut value : Int? = None
for attribute in attributes {
if attribute.name == name {
value = const_eval_attr_i32(attribute)
}
}
value
}
///|
fn const_eval_group_binding(
attributes : Array[Attribute],
) -> (Int, Int)? raise WeslCompileError {
let mut group : Int? = None
let mut binding : Int? = None
for attr in attributes {
if attr.name == "group" {
group = const_eval_attr_i32(attr)
} else if attr.name == "binding" {
binding = const_eval_attr_i32(attr)
}
}
match (group, binding) {
(Some(group), Some(binding)) => Some((group, binding))
_ => None
}
}
///|
fn const_eval_round_up(align : Int, offset : Int) -> Int {
if align <= 1 {
offset
} else {
(offset + align - 1) / align * align
}
}
///|
fn const_eval_member_attr_or_layout(
attributes : Array[Attribute],
name : String,
fallback : Int,
) -> Int? raise WeslCompileError {
match const_eval_named_attr_i32(attributes, name) {
Some(value) => Some(value)
None => Some(fallback)
}
}
///|
fn const_eval_type_align(
unit : TranslationUnit,
type_ : ConstEvalType,
) -> Int? raise WeslCompileError {
match type_ {
Bool | I32 | U32 | F32 => Some(4)
Vector(width, element) =>
match const_eval_type_align(unit, element) {
Some(_) =>
match width {
2 => Some(8)
3 | 4 => Some(16)
_ => None
}
None => None
}
Matrix(_, rows, element) =>
const_eval_type_align(unit, Vector(rows, element))
Array(element, _) => const_eval_type_align(unit, element)
Struct(name) =>
match const_eval_struct_declaration(unit, name) {
Some(struct_) => {
let mut align = 1
for field_decl in struct_.members {
let member_align = match const_eval_member_align(unit, field_decl) {
Some(value) => value
None => return None
}
if member_align > align {
align = member_align
}
}
Some(align)
}
None => None
}
_ => None
}
}
///|
fn const_eval_type_size(
unit : TranslationUnit,
type_ : ConstEvalType,
) -> Int? raise WeslCompileError {
match type_ {
Bool | I32 | U32 | F32 => Some(4)
Vector(width, element) =>
match const_eval_type_size(unit, element) {
Some(element_size) => Some(width * element_size)
None => None
}
Matrix(columns, rows, element) => {
let column_type = ConstEvalType::Vector(rows, element)
let column_stride = match const_eval_type_stride(unit, column_type) {
Some(value) => value
None => return None
}
Some(columns * column_stride)
}
Array(element, Some(count)) => {
let stride = match const_eval_type_stride(unit, element) {
Some(value) => value
None => return None
}
Some(count * stride)
}
Array(_, None) => None
Struct(name) =>
match const_eval_struct_declaration(unit, name) {
Some(struct_) => {
let align = match const_eval_type_align(unit, type_) {
Some(value) => value
None => return None
}
let mut offset = 0
for field_decl in struct_.members {
let member_align = match const_eval_member_align(unit, field_decl) {
Some(value) => value
None => return None
}
let member_size = match const_eval_member_size(unit, field_decl) {
Some(value) => value
None => return None
}
offset = const_eval_round_up(member_align, offset)
offset = offset + member_size
}
Some(const_eval_round_up(align, offset))
}
None => None
}
_ => None
}
}
///|
fn const_eval_type_stride(
unit : TranslationUnit,
type_ : ConstEvalType,
) -> Int? raise WeslCompileError {
let align = match const_eval_type_align(unit, type_) {
Some(value) => value
None => return None
}
let size = match const_eval_type_size(unit, type_) {
Some(value) => value
None => return None
}
Some(const_eval_round_up(align, size))
}
///|
fn const_eval_type_min_size(
unit : TranslationUnit,
type_ : ConstEvalType,
) -> Int? raise WeslCompileError {
match type_ {
Array(element, None) => const_eval_type_stride(unit, element)
_ => const_eval_type_size(unit, type_)
}
}
///|
fn const_eval_member_align(
unit : TranslationUnit,
field_decl : StructMember,
) -> Int? raise WeslCompileError {
let type_ = const_eval_type_source_with_unit(unit, field_decl.type_text)
let fallback = match const_eval_type_align(unit, type_) {
Some(value) => value
None => return None
}
const_eval_member_attr_or_layout(field_decl.attributes, "align", fallback)
}
///|
fn const_eval_member_size(
unit : TranslationUnit,
field_decl : StructMember,
) -> Int? raise WeslCompileError {
let type_ = const_eval_type_source_with_unit(unit, field_decl.type_text)
let fallback = match const_eval_type_min_size(unit, type_) {
Some(value) => value
None => return None
}
const_eval_member_attr_or_layout(field_decl.attributes, "size", fallback)
}
///|
fn const_eval_struct_field(
fields : Array[(String, ConstEvalValue)],
name : String,
) -> ConstEvalValue? {
for field in fields {
let (field_name, value) = field
if field_name == name {
return Some(value)
}
}
None
}
///|
fn const_eval_resource_value(
resources : Array[ResourceBinding],
group : Int,
binding : Int,
) -> ResourceBinding? {
let mut value : ResourceBinding? = None
for resource in resources {
if resource.group == group && resource.binding == binding {
value = Some(resource)
}
}
value
}
///|
fn const_eval_resource_declaration(
unit : TranslationUnit,
group : Int,
binding : Int,
) -> (String, String)? raise WeslCompileError {
for declaration in unit.global_declarations {
match declaration.header {
Var(var_) =>
match
(
var_.name,
var_.type_text,
const_eval_group_binding(declaration.attributes),
) {
(Some(name), Some(type_source), Some((decl_group, decl_binding))) =>
if decl_group == group && decl_binding == binding {
return Some((name, type_source))
}
_ => ()
}
_ => ()
}
}
None
}
///|
fn const_eval_le_u32_at(bytes : Bytes, offset : Int) -> Int64? {
if offset < 0 {
return None
}
if bytes.length() < 4 {
None
} else if offset + 4 > bytes.length() {
None
} else {
let b0 = bytes[offset].to_int64()
let b1 = bytes[offset + 1].to_int64()
let b2 = bytes[offset + 2].to_int64()
let b3 = bytes[offset + 3].to_int64()
Some(b0 | (b1 << 8) | (b2 << 16) | (b3 << 24))
}
}
///|
fn const_eval_scalar_value_from_buffer_at(
ty : ConstEvalType,
data : Bytes,
offset : Int,
) -> ConstEvalValue? {
match ty {
I32 => {
let unsigned = match const_eval_le_u32_at(data, offset) {
Some(value) => value
None => return None
}
let signed = if unsigned >= 2147483648L {
unsigned - 4294967296L
} else {
unsigned
}
Some(I32(signed))
}
U32 => {
let value = match const_eval_le_u32_at(data, offset) {
Some(value) => value
None => return None
}
Some(U32(value))
}
F32 => {
let bits = match const_eval_le_u32_at(data, offset) {
Some(value) => value.to_int()
None => return None
}
Some(F32(Float::reinterpret_from_int(bits)))
}
_ => None
}
}
///|
fn const_eval_value_from_buffer_type(
unit : TranslationUnit,
type_ : ConstEvalType,
data : Bytes,
offset : Int,
) -> ConstEvalValue? raise WeslCompileError {
match type_ {
Vector(width, element) => {
let values : Array[ConstEvalValue] = []
let element_size = match const_eval_type_size(unit, element) {
Some(value) => value
None => return None
}
for index in 0.. values.push(value)
None => return None
}
}
Some(Vector(width, element, values))
}
Matrix(columns, rows, element) => {
let column_type = ConstEvalType::Vector(rows, element)
let column_size = match const_eval_type_size(unit, column_type) {
Some(value) => value
None => return None
}
let column_stride = match const_eval_type_stride(unit, column_type) {
Some(value) => value
None => return None
}
let values : Array[ConstEvalValue] = []
for index in 0.. values.push(value)
None => return None
}
}
ignore(column_size)
Some(Matrix(columns, rows, element, values))
}
Array(element, count) => {
let element_size = match const_eval_type_size(unit, element) {
Some(value) => value
None => return None
}
let stride = match const_eval_type_stride(unit, element) {
Some(value) => value
None => return None
}
let element_count = match count {
Some(value) => value
None => {
let remaining = data.length() - offset
if remaining < stride {
return None
}
remaining / stride
}
}
if element_count <= 0 {
return None
}
let values : Array[ConstEvalValue] = []
for index in 0.. data.length() {
return None
}
match
const_eval_value_from_buffer_type(unit, element, data, element_offset) {
Some(value) => values.push(value)
None => return None
}
}
Some(Array(element, count, values))
}
Struct(name) =>
match const_eval_struct_declaration(unit, name) {
Some(struct_) => {
let fields : Array[(String, ConstEvalValue)] = []
let mut cursor = offset
for field_decl in struct_.members {
let member_type = const_eval_type_source_with_unit(
unit,
field_decl.type_text,
)
let member_align = match const_eval_member_align(unit, field_decl) {
Some(value) => value
None => return None
}
cursor = const_eval_round_up(member_align, cursor)
if member_type is Array(_, None) {
match
const_eval_value_from_buffer_type(
unit, member_type, data, cursor,
) {
Some(value) => fields.push((field_decl.name, value))
None => return None
}
cursor = data.length()
} else {
let member_size = match const_eval_member_size(unit, field_decl) {
Some(value) => value
None => return None
}
match
const_eval_value_from_buffer_type(
unit, member_type, data, cursor,
) {
Some(value) => fields.push((field_decl.name, value))
None => return None
}
cursor = cursor + member_size
}
}
Some(Struct(name, fields))
}
None => None
}
scalar => const_eval_scalar_value_from_buffer_at(scalar, data, offset)
}
}
///|
fn const_eval_value_from_buffer_with_unit(
unit : TranslationUnit,
ty : String,
data : Bytes,
) -> ConstEvalValue? raise WeslCompileError {
const_eval_value_from_buffer_type(
unit,
const_eval_type_source_with_unit(unit, ty),
data,
0,
)
}
///|
fn const_eval_context_from_translation_unit_with_exec_inputs(
unit : TranslationUnit,
resources : Array[ResourceBinding],
overrides : Array[OverrideValue],
) -> ConstEvalContext raise WeslCompileError {
let ctx = ConstEvalContext::new()
for declaration in unit.global_declarations {
match declaration.header {
Function(function_) =>
if syntax_has_attribute(declaration.attributes, "const") {
ctx.functions.set(
function_.name,
const_eval_function_from_source(declaration.source),
)
}
_ => ()
}
}
for declaration in unit.global_declarations {
match declaration.header {
Var(var_) =>
match
(
var_.name,
var_.type_text,
const_eval_group_binding(declaration.attributes),
) {
(Some(name), Some(type_source), Some((group, binding))) =>
match const_eval_resource_value(resources, group, binding) {
Some(resource) =>
match
const_eval_value_from_buffer_with_unit(
unit,
type_source,
resource.data,
) {
Some(value) => ctx.bindings.set(name, value)
None =>
raise Validation(
"resource binding group=\{group} binding=\{binding} is incompatible with `\{type_source}`",
)
}
None => ()
}
_ => ()
}
Const(const_) =>
match const_.initializer {
Some(initializer) => {
let value = const_eval_parse_expression_value(initializer, ctx)
let stored = match const_.type_text {
Some(type_source) =>
const_eval_convert_to_type(
value,
const_eval_parse_type_source(type_source),
const_.name,
)
None => value
}
ctx.bindings.set(const_.name, stored)
}
None => ()
}
_ => ()
}
}
for declaration in unit.global_declarations {
match declaration.header {
Override(override_) => {
let source = match
const_eval_override_value(overrides, override_.name) {
Some(value) => Some(value)
None => override_.initializer
}
match source {
Some(initializer) => {
let value = const_eval_parse_expression_value(initializer, ctx)
let stored = match override_.type_text {
Some(type_source) =>
const_eval_convert_to_type(
value,
const_eval_parse_type_source(type_source),
override_.name,
)
None => value
}
ctx.bindings.set(override_.name, stored)
}
None => raise Validation("uninitialized override `\{override_.name}`")
}
}
_ => ()
}
}
ctx
}
///|
fn const_eval_from_translation_unit_value(
unit : TranslationUnit,
target_expr : String,
) -> ConstEvalValue raise WeslCompileError {
let ctx = const_eval_context_from_translation_unit(unit)
const_eval_parse_expression_value(target_expr, ctx)
}
///|
fn const_eval_first_four_bytes(bytes : Bytes) -> Bytes {
let array = bytes.to_array()
Bytes::from_array([array[0], array[1], array[2], array[3]])
}
///|
fn const_eval_i32_buffer(number : Int64) -> Bytes {
const_eval_first_four_bytes(number.reinterpret_as_uint64().to_le_bytes())
}
///|
fn const_eval_u32_buffer(number : Int64) -> Bytes {
const_eval_first_four_bytes(number.reinterpret_as_uint64().to_le_bytes())
}
///|
fn const_eval_value_to_buffer(value : ConstEvalValue) -> Bytes? {
match value {
I32(number) => Some(const_eval_i32_buffer(number))
U32(number) => Some(const_eval_u32_buffer(number))
F32(number) => Some(number.to_le_bytes())
Vector(_, _, elements) => {
let bytes : Array[Byte] = []
for element in elements {
match const_eval_value_to_buffer(element) {
Some(element_bytes) =>
for byte in element_bytes.to_array() {
bytes.push(byte)
}
None => return None
}
}
Some(Bytes::from_array(bytes))
}
_ => None
}
}
///|
fn const_eval_value_to_buffer_with_unit(
unit : TranslationUnit,
value : ConstEvalValue,
) -> Bytes? raise WeslCompileError {
match value {
I32(_) | U32(_) | F32(_) => const_eval_value_to_buffer(value)
Vector(_, _, elements) => {
let bytes : Array[Byte] = []
for element in elements {
match const_eval_value_to_buffer_with_unit(unit, element) {
Some(element_bytes) =>
for byte in element_bytes.to_array() {
bytes.push(byte)
}
None => return None
}
}
Some(Bytes::from_array(bytes))
}
Matrix(_, rows, element, columns) => {
let column_type = ConstEvalType::Vector(rows, element)
let column_size = match const_eval_type_size(unit, column_type) {
Some(value) => value
None => return None
}
let column_stride = match const_eval_type_stride(unit, column_type) {
Some(value) => value
None => return None
}
let bytes : Array[Byte] = []
for column in columns {
let column_bytes = match
const_eval_value_to_buffer_with_unit(unit, column) {
Some(value) => value
None => return None
}
for byte in column_bytes.to_array() {
bytes.push(byte)
}
for _ in column_size.. {
match count {
Some(expected) => if expected != elements.length() { return None }
None => ()
}
let element_size = match const_eval_type_size(unit, element) {
Some(value) => value
None => return None
}
let stride = match const_eval_type_stride(unit, element) {
Some(value) => value
None => return None
}
let bytes : Array[Byte] = []
for element_value in elements {
let element_bytes = match
const_eval_value_to_buffer_with_unit(unit, element_value) {
Some(value) => value
None => return None
}
for byte in element_bytes.to_array() {
bytes.push(byte)
}
for _ in element_size..
match const_eval_struct_declaration(unit, name) {
Some(struct_) => {
let bytes : Array[Byte] = []
for field_decl in struct_.members {
let field = match const_eval_struct_field(fields, field_decl.name) {
Some(value) => value
None => return None
}
let align = match const_eval_member_align(unit, field_decl) {
Some(value) => value
None => return None
}
let field_offset = const_eval_round_up(align, bytes.length())
for _ in bytes.length().. value
None => return None
}
for byte in field_bytes.to_array() {
bytes.push(byte)
}
let size = match field {
Array(element, None, elements) => {
let stride = match const_eval_type_stride(unit, element) {
Some(value) => value
None => return None
}
stride * elements.length()
}
_ =>
match const_eval_member_size(unit, field_decl) {
Some(value) => value
None => return None
}
}
for _ in field_bytes.length().. None
}
_ => None
}
}
///|
fn const_eval_result(value : ConstEvalValue) -> EvalResult {
{
value: const_eval_render_value(value),
buffer: const_eval_value_to_buffer(value),
}
}
///|
fn const_eval_result_with_unit(
unit : TranslationUnit,
value : ConstEvalValue,
) -> EvalResult raise WeslCompileError {
{
value: const_eval_render_value(value),
buffer: const_eval_value_to_buffer_with_unit(unit, value),
}
}
///|
fn const_eval_attribute_identifier(attr : Attribute) -> String? {
match attr.arguments {
Some(text) => {
let trimmed = text.trim()
if trimmed.length() >= 2 &&
trimmed[:1].to_owned() == "\"" &&
trimmed[trimmed.length() - 1:trimmed.length()].to_owned() == "\"" {
Some(trimmed[1:trimmed.length() - 1].to_owned())
} else {
Some(trimmed.to_owned())
}
}
None => None
}
}
///|
fn const_eval_parameter_builtin(parameter : FunctionParameter) -> String? {
for attr in parameter.attributes {
if attr.name == "builtin" {
return const_eval_attribute_identifier(attr)
}
}
None
}
///|
fn const_eval_parameter_location(
parameter : FunctionParameter,
) -> Int? raise WeslCompileError {
const_eval_named_attr_i32(parameter.attributes, "location")
}
///|
fn const_eval_int_input_value(
number : Int,
type_ : ConstEvalType,
context : String,
) -> ConstEvalValue raise WeslCompileError {
match type_ {
I32 => I32(number.to_int64())
U32 => {
if number < 0 {
raise Validation(
"negative builtin input for u32 parameter `\{context}`",
)
}
U32(number.to_int64())
}
F32 => F32(Float::from_double(number.to_double()))
AbstractInt => AbstractInt(number.to_int64())
_ =>
raise Validation(
"builtin input `\{context}` is incompatible with `\{type_.label()}`",
)
}
}
///|
fn const_eval_double_input_value(
number : Double,
type_ : ConstEvalType,
context : String,
) -> ConstEvalValue raise WeslCompileError {
match type_ {
F32 => F32(Float::from_double(number))
AbstractFloat => AbstractFloat(number, true)
_ =>
raise Validation(
"builtin input `\{context}` is incompatible with `\{type_.label()}`",
)
}
}
///|
fn const_eval_int_vector_input_value(
values : Array[Int],
type_ : ConstEvalType,
context : String,
) -> ConstEvalValue raise WeslCompileError {
match type_ {
Vector(width, element) => {
if values.length() < width {
raise Validation(
"builtin input `\{context}` does not provide \{width} components",
)
}
let elements : Array[ConstEvalValue] = []
for index in 0..
raise Validation(
"builtin input `\{context}` is incompatible with `\{type_.label()}`",
)
}
}
///|
fn const_eval_double_vector_input_value(
values : Array[Double],
type_ : ConstEvalType,
context : String,
) -> ConstEvalValue raise WeslCompileError {
match type_ {
Vector(width, element) => {
if values.length() < width {
raise Validation(
"builtin input `\{context}` does not provide \{width} components",
)
}
let elements : Array[ConstEvalValue] = []
for index in 0..
raise Validation(
"builtin input `\{context}` is incompatible with `\{type_.label()}`",
)
}
}
///|
fn const_eval_user_input_value(
input : UserInputValue,
type_ : ConstEvalType,
context : String,
) -> ConstEvalValue raise WeslCompileError {
match input {
Bool(value) =>
match type_ {
Bool => Bool(value)
_ =>
raise Validation(
"user input `\{context}` is incompatible with `\{type_.label()}`",
)
}
I32(value) => const_eval_int_input_value(value, type_, context)
U32(value) => const_eval_int_input_value(value, type_, context)
F32(value) => const_eval_double_input_value(value, type_, context)
I32Vector(values) =>
const_eval_int_vector_input_value(values, type_, context)
U32Vector(values) =>
const_eval_int_vector_input_value(values, type_, context)
F32Vector(values) =>
const_eval_double_vector_input_value(values, type_, context)
}
}
///|
fn const_eval_user_defined_input(
inputs : Inputs,
location : Int,
) -> UserInputValue? {
let mut value : UserInputValue? = None
for input in inputs.user_defined {
if input.location == location {
value = Some(input.value)
}
}
value
}
///|
fn const_eval_location_input_value(
inputs : Inputs,
location : Int,
type_ : ConstEvalType,
) -> ConstEvalValue raise WeslCompileError {
let input = match const_eval_user_defined_input(inputs, location) {
Some(value) => value
None =>
raise Validation("missing user-defined input at location \{location}")
}
const_eval_user_input_value(input, type_, "location \{location}")
}
///|
fn const_eval_struct_member_input_value(
unit : TranslationUnit,
inputs : Inputs,
field : StructMember,
) -> ConstEvalValue raise WeslCompileError {
let type_ = const_eval_type_source_with_unit(unit, field.type_text)
let builtin = {
let mut value : String? = None
for attr in field.attributes {
if attr.name == "builtin" {
value = const_eval_attribute_identifier(attr)
}
}
value
}
match builtin {
Some(name) => const_eval_builtin_input_value(inputs, name, type_)
None =>
match const_eval_named_attr_i32(field.attributes, "location") {
Some(location) =>
const_eval_location_input_value(inputs, location, type_)
None =>
raise Validation(
"entrypoint struct member `\{field.name}` is missing a supported @builtin or @location attribute",
)
}
}
}
///|
fn const_eval_struct_entrypoint_input_value(
unit : TranslationUnit,
inputs : Inputs,
type_ : ConstEvalType,
) -> ConstEvalValue? raise WeslCompileError {
match type_ {
Struct(name) =>
match const_eval_struct_declaration(unit, name) {
Some(struct_) => {
let fields : Array[(String, ConstEvalValue)] = []
for field in struct_.members {
fields.push(
(
field.name,
const_eval_struct_member_input_value(unit, inputs, field),
),
)
}
Some(Struct(name, fields))
}
None => None
}
_ => None
}
}
///|
fn[A] const_eval_required_input(
value : A?,
name : String,
) -> A raise WeslCompileError {
match value {
Some(value) => value
None => raise Validation("missing builtin input `\{name}`")
}
}
///|
fn const_eval_builtin_input_value(
inputs : Inputs,
builtin : String,
type_ : ConstEvalType,
) -> ConstEvalValue raise WeslCompileError {
match builtin {
"vertex_index" =>
const_eval_int_input_value(
const_eval_required_input(inputs.vertex_index, builtin),
type_,
builtin,
)
"instance_index" =>
const_eval_int_input_value(
const_eval_required_input(inputs.instance_index, builtin),
type_,
builtin,
)
"position" =>
const_eval_double_vector_input_value(
const_eval_required_input(inputs.position, builtin),
type_,
builtin,
)
"front_facing" =>
match type_ {
Bool => Bool(const_eval_required_input(inputs.front_facing, builtin))
_ =>
raise Validation(
"builtin input `front_facing` is incompatible with `\{type_.label()}`",
)
}
"sample_index" =>
const_eval_int_input_value(
const_eval_required_input(inputs.sample_index, builtin),
type_,
builtin,
)
"sample_mask" =>
const_eval_int_input_value(
const_eval_required_input(inputs.sample_mask, builtin),
type_,
builtin,
)
"local_invocation_id" =>
const_eval_int_vector_input_value(
const_eval_required_input(inputs.local_invocation_id, builtin),
type_,
builtin,
)
"local_invocation_index" =>
const_eval_int_input_value(
const_eval_required_input(inputs.local_invocation_index, builtin),
type_,
builtin,
)
"global_invocation_id" =>
const_eval_int_vector_input_value(
const_eval_required_input(inputs.global_invocation_id, builtin),
type_,
builtin,
)
"workgroup_id" =>
const_eval_int_vector_input_value(
const_eval_required_input(inputs.workgroup_id, builtin),
type_,
builtin,
)
"num_workgroups" =>
const_eval_int_vector_input_value(
const_eval_required_input(inputs.num_workgroups, builtin),
type_,
builtin,
)
"subgroup_invocation_id" =>
const_eval_int_input_value(
const_eval_required_input(inputs.subgroup_invocation_id, builtin),
type_,
builtin,
)
"subgroup_size" =>
const_eval_int_input_value(
const_eval_required_input(inputs.subgroup_size, builtin),
type_,
builtin,
)
"subgroup_id" =>
const_eval_int_input_value(
const_eval_required_input(inputs.subgroup_id, builtin),
type_,
builtin,
)
"num_subgroups" =>
const_eval_int_input_value(
const_eval_required_input(inputs.num_subgroups, builtin),
type_,
builtin,
)
"primitive_index" =>
const_eval_int_input_value(
const_eval_required_input(inputs.primitive_index, builtin),
type_,
builtin,
)
"view_index" =>
const_eval_int_input_value(
const_eval_required_input(inputs.view_index, builtin),
type_,
builtin,
)
_ => raise Validation("unsupported builtin input `\{builtin}`")
}
}
///|
fn const_eval_exec_entrypoint_args(
unit : TranslationUnit,
function_ : FunctionDeclaration,
inputs : Inputs,
) -> Array[ConstEvalValue] raise WeslCompileError {
let args : Array[ConstEvalValue] = []
for parameter in function_.parameters {
let type_ = const_eval_type_source_with_unit(unit, parameter.type_text)
match const_eval_parameter_builtin(parameter) {
Some(builtin) =>
args.push(const_eval_builtin_input_value(inputs, builtin, type_))
None =>
match const_eval_parameter_location(parameter) {
Some(location) =>
args.push(const_eval_location_input_value(inputs, location, type_))
None =>
match
const_eval_struct_entrypoint_input_value(unit, inputs, type_) {
Some(value) => args.push(value)
None =>
raise Validation(
"entrypoint parameter `\{parameter.name}` is missing a supported @builtin or @location attribute",
)
}
}
}
}
args
}
///|
fn const_eval_function_source_from_declaration(
function_ : FunctionDeclaration,
) -> String {
let params : Array[String] = []
for parameter in function_.parameters {
params.push("\{parameter.name}: \{parameter.type_text}")
}
let joined_params = params.join(", ")
let return_type = match function_.return_type {
Some(type_) => type_
None => "void"
}
let parts : Array[String] = [
"@const fn \{function_.name}(\{joined_params}) -> \{return_type} {",
]
for statement in function_.body.statements {
parts.push(statement.source)
}
parts.push("}")
parts.join("\n")
}
///|
fn const_eval_exec_entrypoint(
unit : TranslationUnit,
entrypoint : String,
inputs : Inputs,
resources : Array[ResourceBinding],
overrides : Array[OverrideValue],
) -> ConstEvalEntryExec raise WeslCompileError {
for declaration in unit.global_declarations {
match declaration.header {
Function(function_) =>
if function_.name == entrypoint {
let ctx = const_eval_context_from_translation_unit_with_exec_inputs(
unit, resources, overrides,
)
match function_.return_type {
None | Some("void") => {
let source = const_eval_function_source_from_declaration(
function_,
)
let function_value = const_eval_function_from_source(source)
let args = const_eval_exec_entrypoint_args(
unit, function_, inputs,
)
return {
value: const_eval_execute_function(function_value, args, ctx),
ctx,
}
}
Some(_) => {
let source = const_eval_function_source_from_declaration(
function_,
)
let function_value = const_eval_function_from_source(source)
let args = const_eval_exec_entrypoint_args(
unit, function_, inputs,
)
return {
value: Some(
const_eval_execute_function_value(function_value, args, ctx),
),
ctx,
}
}
}
}
_ => ()
}
}
raise Validation("unknown function `\{entrypoint}`")
}
///|
fn const_eval_resources_from_exec_context(
unit : TranslationUnit,
resources : Array[ResourceBinding],
ctx : ConstEvalContext,
) -> Array[ResourceBinding] raise WeslCompileError {
let updated : Array[ResourceBinding] = []
for resource in resources {
if resource.kind.contains("storage") {
match
const_eval_resource_declaration(unit, resource.group, resource.binding) {
Some((name, _type_source)) =>
match ctx.bindings.get(name) {
Some(value) =>
match const_eval_value_to_buffer_with_unit(unit, value) {
Some(data) =>
updated.push({
group: resource.group,
binding: resource.binding,
kind: resource.kind,
data,
})
None =>
raise Validation(
"resource binding group=\{resource.group} binding=\{resource.binding} cannot be written back",
)
}
None => updated.push(resource)
}
None => updated.push(resource)
}
} else {
updated.push(resource)
}
}
updated
}
///|
pub fn eval_const(
source : String,
target_expr : String,
) -> EvalResult raise WeslCompileError {
const_eval_result(eval_const_function_value(source, target_expr))
}
///|
pub fn eval_str(source : String) -> EvalResult raise WeslCompileError {
const_eval_result(eval_const_expression_value(source))
}
///|
pub fn CompileResult::eval(
self : CompileResult,
source : String,
) -> EvalResult raise WeslCompileError {
const_eval_result_with_unit(
self.syntax,
const_eval_from_translation_unit_value(self.syntax, source),
)
}
///|
pub fn CompileResult::exec(
self : CompileResult,
entrypoint : String,
inputs : Inputs,
resources : Array[ResourceBinding],
overrides : Array[OverrideValue],
) -> ExecResult raise WeslCompileError {
let inst = const_eval_exec_entrypoint(
self.syntax,
entrypoint,
inputs,
resources,
overrides,
)
let value = match inst.value {
Some(value) => Some(const_eval_render_value(value))
None => None
}
let buffer = match inst.value {
Some(value) => const_eval_value_to_buffer_with_unit(self.syntax, value)
None => None
}
{
value,
buffer,
resources: const_eval_resources_from_exec_context(
self.syntax,
resources,
inst.ctx,
),
}
}