use alloc::{borrow::Cow, string::String, vec::Vec};
use core::fmt;
use super::{builtins::MacroCall, Span};
use crate::{
AddressSpace, BinaryOperator, Binding, Constant, Expression, Function, GlobalVariable, Handle,
Interpolation, Literal, Sampling, StorageAccess, Type, UnaryOperator,
};
#[derive(Debug, Clone, Copy)]
pub enum GlobalLookupKind {
Variable(Handle<GlobalVariable>),
Constant(Handle<Constant>, Handle<Type>),
BlockSelect(Handle<GlobalVariable>, u32),
}
#[derive(Debug, Clone, Copy)]
pub struct GlobalLookup {
pub kind: GlobalLookupKind,
pub entry_arg: Option<usize>,
pub mutable: bool,
}
#[derive(Debug, Clone)]
pub struct ParameterInfo {
pub qualifier: ParameterQualifier,
pub depth: bool,
}
#[derive(Clone, Copy)]
pub enum FunctionKind {
Call(Handle<Function>),
Macro(MacroCall),
}
impl fmt::Debug for FunctionKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match *self {
Self::Call(_) => write!(f, "Call"),
Self::Macro(_) => write!(f, "Macro"),
}
}
}
#[derive(Debug)]
pub struct Overload {
pub parameters: Vec<Handle<Type>>,
pub parameters_info: Vec<ParameterInfo>,
pub kind: FunctionKind,
pub defined: bool,
pub internal: bool,
pub void: bool,
}
bitflags::bitflags! {
#[derive(Default)]
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub struct BuiltinVariations: u32 {
const STANDARD = 1 << 0;
const DOUBLE = 1 << 1;
const CUBE_TEXTURES_ARRAY = 1 << 2;
const D2_MULTI_TEXTURES_ARRAY = 1 << 3;
}
}
#[derive(Debug, Default)]
pub struct FunctionDeclaration {
pub overloads: Vec<Overload>,
pub variations: BuiltinVariations,
}
#[derive(Debug)]
pub struct EntryArg {
pub name: Option<String>,
pub binding: Binding,
pub handle: Handle<GlobalVariable>,
pub storage: StorageQualifier,
}
#[derive(Debug, Clone)]
pub struct VariableReference {
pub expr: Handle<Expression>,
pub load: bool,
pub mutable: bool,
pub constant: Option<(Handle<Constant>, Handle<Type>)>,
pub entry_arg: Option<usize>,
}
#[derive(Debug, Clone)]
pub struct HirExpr {
pub kind: HirExprKind,
pub meta: Span,
}
#[derive(Debug, Clone)]
pub enum HirExprKind {
Access {
base: Handle<HirExpr>,
index: Handle<HirExpr>,
},
Select {
base: Handle<HirExpr>,
field: String,
},
Literal(Literal),
Binary {
left: Handle<HirExpr>,
op: BinaryOperator,
right: Handle<HirExpr>,
},
Unary {
op: UnaryOperator,
expr: Handle<HirExpr>,
},
Variable(VariableReference),
Call(FunctionCall),
Conditional {
condition: Handle<HirExpr>,
accept: Handle<HirExpr>,
reject: Handle<HirExpr>,
},
Assign {
tgt: Handle<HirExpr>,
value: Handle<HirExpr>,
},
PrePostfix {
op: BinaryOperator,
postfix: bool,
expr: Handle<HirExpr>,
},
Method {
expr: Handle<HirExpr>,
name: String,
args: Vec<Handle<HirExpr>>,
},
}
#[derive(Debug, Hash, PartialEq, Eq)]
pub enum QualifierKey<'a> {
String(Cow<'a, str>),
Layout,
Format,
Index,
}
#[derive(Debug)]
pub enum QualifierValue {
None,
Uint(u32),
Layout(StructLayout),
Format(crate::StorageFormat),
}
#[derive(Debug, Default)]
pub struct TypeQualifiers<'a> {
pub span: Span,
pub storage: (StorageQualifier, Span),
pub invariant: Option<Span>,
pub interpolation: Option<(Interpolation, Span)>,
pub precision: Option<(Precision, Span)>,
pub sampling: Option<(Sampling, Span)>,
pub storage_access: Option<(StorageAccess, Span)>,
pub layout_qualifiers: crate::FastHashMap<QualifierKey<'a>, (QualifierValue, Span)>,
}
impl<'a> TypeQualifiers<'a> {
pub fn unused_errors(&self, errors: &mut Vec<super::Error>) {
if let Some(meta) = self.invariant {
errors.push(super::Error {
kind: super::ErrorKind::SemanticError(
"Invariant qualifier can only be used in in/out variables".into(),
),
meta,
});
}
if let Some((_, meta)) = self.interpolation {
errors.push(super::Error {
kind: super::ErrorKind::SemanticError(
"Interpolation qualifiers can only be used in in/out variables".into(),
),
meta,
});
}
if let Some((_, meta)) = self.sampling {
errors.push(super::Error {
kind: super::ErrorKind::SemanticError(
"Sampling qualifiers can only be used in in/out variables".into(),
),
meta,
});
}
if let Some((_, meta)) = self.storage_access {
errors.push(super::Error {
kind: super::ErrorKind::SemanticError(
"Memory qualifiers can only be used in storage variables".into(),
),
meta,
});
}
for &(_, meta) in self.layout_qualifiers.values() {
errors.push(super::Error {
kind: super::ErrorKind::SemanticError("Unexpected qualifier".into()),
meta,
});
}
}
pub fn uint_layout_qualifier(
&mut self,
name: &'a str,
errors: &mut Vec<super::Error>,
) -> Option<u32> {
match self
.layout_qualifiers
.remove(&QualifierKey::String(name.into()))
{
Some((QualifierValue::Uint(v), _)) => Some(v),
Some((_, meta)) => {
errors.push(super::Error {
kind: super::ErrorKind::SemanticError("Qualifier expects a uint value".into()),
meta,
});
Some(0)
}
_ => None,
}
}
pub fn none_layout_qualifier(&mut self, name: &'a str, errors: &mut Vec<super::Error>) -> bool {
match self
.layout_qualifiers
.remove(&QualifierKey::String(name.into()))
{
Some((QualifierValue::None, _)) => true,
Some((_, meta)) => {
errors.push(super::Error {
kind: super::ErrorKind::SemanticError(
"Qualifier doesn't expect a value".into(),
),
meta,
});
true
}
_ => false,
}
}
}
#[derive(Debug, Clone)]
pub enum FunctionCallKind {
TypeConstructor(Handle<Type>),
Function(String),
}
#[derive(Debug, Clone)]
pub struct FunctionCall {
pub kind: FunctionCallKind,
pub args: Vec<Handle<HirExpr>>,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum StorageQualifier {
AddressSpace(AddressSpace),
Input,
Output,
Const,
}
impl Default for StorageQualifier {
fn default() -> Self {
StorageQualifier::AddressSpace(AddressSpace::Function)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum StructLayout {
Std140,
Std430,
}
#[derive(Debug, Clone, PartialEq, Copy)]
pub enum Precision {
Low,
Medium,
High,
}
#[derive(Debug, Clone, PartialEq, Copy)]
pub enum ParameterQualifier {
In,
Out,
InOut,
Const,
}
impl ParameterQualifier {
pub const fn is_lhs(&self) -> bool {
match *self {
ParameterQualifier::Out | ParameterQualifier::InOut => true,
_ => false,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Profile {
Core,
}