1mod error;
8mod index;
9mod lower;
10mod parse;
11#[cfg(test)]
12mod tests;
13
14use crate::front::wgsl::error::Error;
15use crate::front::wgsl::parse::Parser;
16use thiserror::Error;
17
18pub use crate::front::wgsl::error::ParseError;
19use crate::front::wgsl::lower::Lowerer;
20
21pub struct Frontend {
22 parser: Parser,
23}
24
25impl Frontend {
26 pub const fn new() -> Self {
27 Self {
28 parser: Parser::new(),
29 }
30 }
31
32 pub fn parse(&mut self, source: &str) -> Result<crate::Module, ParseError> {
33 self.inner(source).map_err(|x| x.as_parse_error(source))
34 }
35
36 fn inner<'a>(&mut self, source: &'a str) -> Result<crate::Module, Error<'a>> {
37 let tu = self.parser.parse(source)?;
38 let index = index::Index::generate(&tu)?;
39 let module = Lowerer::new(&index).lower(&tu)?;
40
41 Ok(module)
42 }
43}
44
45pub fn parse_str(source: &str) -> Result<crate::Module, ParseError> {
46 Frontend::new().parse(source)
47}
48
49impl crate::StorageFormat {
50 const fn to_wgsl(self) -> &'static str {
51 use crate::StorageFormat as Sf;
52 match self {
53 Sf::R8Unorm => "r8unorm",
54 Sf::R8Snorm => "r8snorm",
55 Sf::R8Uint => "r8uint",
56 Sf::R8Sint => "r8sint",
57 Sf::R16Uint => "r16uint",
58 Sf::R16Sint => "r16sint",
59 Sf::R16Float => "r16float",
60 Sf::Rg8Unorm => "rg8unorm",
61 Sf::Rg8Snorm => "rg8snorm",
62 Sf::Rg8Uint => "rg8uint",
63 Sf::Rg8Sint => "rg8sint",
64 Sf::R32Uint => "r32uint",
65 Sf::R32Sint => "r32sint",
66 Sf::R32Float => "r32float",
67 Sf::Rg16Uint => "rg16uint",
68 Sf::Rg16Sint => "rg16sint",
69 Sf::Rg16Float => "rg16float",
70 Sf::Rgba8Unorm => "rgba8unorm",
71 Sf::Rgba8Snorm => "rgba8snorm",
72 Sf::Rgba8Uint => "rgba8uint",
73 Sf::Rgba8Sint => "rgba8sint",
74 Sf::Rgb10a2Unorm => "rgb10a2unorm",
75 Sf::Rg11b10Float => "rg11b10float",
76 Sf::Rg32Uint => "rg32uint",
77 Sf::Rg32Sint => "rg32sint",
78 Sf::Rg32Float => "rg32float",
79 Sf::Rgba16Uint => "rgba16uint",
80 Sf::Rgba16Sint => "rgba16sint",
81 Sf::Rgba16Float => "rgba16float",
82 Sf::Rgba32Uint => "rgba32uint",
83 Sf::Rgba32Sint => "rgba32sint",
84 Sf::Rgba32Float => "rgba32float",
85 Sf::R16Unorm => "r16unorm",
86 Sf::R16Snorm => "r16snorm",
87 Sf::Rg16Unorm => "rg16unorm",
88 Sf::Rg16Snorm => "rg16snorm",
89 Sf::Rgba16Unorm => "rgba16unorm",
90 Sf::Rgba16Snorm => "rgba16snorm",
91 }
92 }
93}
94
95impl crate::TypeInner {
96 fn to_wgsl(&self, gctx: crate::proc::GlobalCtx) -> String {
102 use crate::TypeInner as Ti;
103
104 match *self {
105 Ti::Scalar { kind, width } => kind.to_wgsl(width),
106 Ti::Vector { size, kind, width } => {
107 format!("vec{}<{}>", size as u32, kind.to_wgsl(width))
108 }
109 Ti::Matrix {
110 columns,
111 rows,
112 width,
113 } => {
114 format!(
115 "mat{}x{}<{}>",
116 columns as u32,
117 rows as u32,
118 crate::ScalarKind::Float.to_wgsl(width),
119 )
120 }
121 Ti::Atomic { kind, width } => {
122 format!("atomic<{}>", kind.to_wgsl(width))
123 }
124 Ti::Pointer { base, .. } => {
125 let base = &gctx.types[base];
126 let name = base.name.as_deref().unwrap_or("unknown");
127 format!("ptr<{name}>")
128 }
129 Ti::ValuePointer { kind, width, .. } => {
130 format!("ptr<{}>", kind.to_wgsl(width))
131 }
132 Ti::Array { base, size, .. } => {
133 let member_type = &gctx.types[base];
134 let base = member_type.name.as_deref().unwrap_or("unknown");
135 match size {
136 crate::ArraySize::Constant(size) => format!("array<{base}, {size}>"),
137 crate::ArraySize::Dynamic => format!("array<{base}>"),
138 }
139 }
140 Ti::Struct { .. } => {
141 "struct".to_string()
143 }
144 Ti::Image {
145 dim,
146 arrayed,
147 class,
148 } => {
149 let dim_suffix = match dim {
150 crate::ImageDimension::D1 => "_1d",
151 crate::ImageDimension::D2 => "_2d",
152 crate::ImageDimension::D3 => "_3d",
153 crate::ImageDimension::Cube => "_cube",
154 };
155 let array_suffix = if arrayed { "_array" } else { "" };
156
157 let class_suffix = match class {
158 crate::ImageClass::Sampled { multi: true, .. } => "_multisampled",
159 crate::ImageClass::Depth { multi: false } => "_depth",
160 crate::ImageClass::Depth { multi: true } => "_depth_multisampled",
161 crate::ImageClass::Sampled { multi: false, .. }
162 | crate::ImageClass::Storage { .. } => "",
163 };
164
165 let type_in_brackets = match class {
166 crate::ImageClass::Sampled { kind, .. } => {
167 let element_type = kind.to_wgsl(4);
171 format!("<{element_type}>")
172 }
173 crate::ImageClass::Depth { multi: _ } => String::new(),
174 crate::ImageClass::Storage { format, access } => {
175 if access.contains(crate::StorageAccess::STORE) {
176 format!("<{},write>", format.to_wgsl())
177 } else {
178 format!("<{}>", format.to_wgsl())
179 }
180 }
181 };
182
183 format!("texture{class_suffix}{dim_suffix}{array_suffix}{type_in_brackets}")
184 }
185 Ti::Sampler { .. } => "sampler".to_string(),
186 Ti::AccelerationStructure => "acceleration_structure".to_string(),
187 Ti::RayQuery => "ray_query".to_string(),
188 Ti::BindingArray { base, size, .. } => {
189 let member_type = &gctx.types[base];
190 let base = member_type.name.as_deref().unwrap_or("unknown");
191 match size {
192 crate::ArraySize::Constant(size) => format!("binding_array<{base}, {size}>"),
193 crate::ArraySize::Dynamic => format!("binding_array<{base}>"),
194 }
195 }
196 }
197 }
198}
199
200mod type_inner_tests {
201
202 #[test]
203 fn to_wgsl() {
204 use std::num::NonZeroU32;
205
206 let mut types = crate::UniqueArena::new();
207
208 let mytype1 = types.insert(
209 crate::Type {
210 name: Some("MyType1".to_string()),
211 inner: crate::TypeInner::Struct {
212 members: vec![],
213 span: 0,
214 },
215 },
216 Default::default(),
217 );
218 let mytype2 = types.insert(
219 crate::Type {
220 name: Some("MyType2".to_string()),
221 inner: crate::TypeInner::Struct {
222 members: vec![],
223 span: 0,
224 },
225 },
226 Default::default(),
227 );
228
229 let gctx = crate::proc::GlobalCtx {
230 types: &types,
231 constants: &crate::Arena::new(),
232 const_expressions: &crate::Arena::new(),
233 };
234 let array = crate::TypeInner::Array {
235 base: mytype1,
236 stride: 4,
237 size: crate::ArraySize::Constant(unsafe { NonZeroU32::new_unchecked(32) }),
238 };
239 assert_eq!(array.to_wgsl(gctx), "array<MyType1, 32>");
240
241 let mat = crate::TypeInner::Matrix {
242 rows: crate::VectorSize::Quad,
243 columns: crate::VectorSize::Bi,
244 width: 8,
245 };
246 assert_eq!(mat.to_wgsl(gctx), "mat2x4<f64>");
247
248 let ptr = crate::TypeInner::Pointer {
249 base: mytype2,
250 space: crate::AddressSpace::Storage {
251 access: crate::StorageAccess::default(),
252 },
253 };
254 assert_eq!(ptr.to_wgsl(gctx), "ptr<MyType2>");
255
256 let img1 = crate::TypeInner::Image {
257 dim: crate::ImageDimension::D2,
258 arrayed: false,
259 class: crate::ImageClass::Sampled {
260 kind: crate::ScalarKind::Float,
261 multi: true,
262 },
263 };
264 assert_eq!(img1.to_wgsl(gctx), "texture_multisampled_2d<f32>");
265
266 let img2 = crate::TypeInner::Image {
267 dim: crate::ImageDimension::Cube,
268 arrayed: true,
269 class: crate::ImageClass::Depth { multi: false },
270 };
271 assert_eq!(img2.to_wgsl(gctx), "texture_depth_cube_array");
272
273 let img3 = crate::TypeInner::Image {
274 dim: crate::ImageDimension::D2,
275 arrayed: false,
276 class: crate::ImageClass::Depth { multi: true },
277 };
278 assert_eq!(img3.to_wgsl(gctx), "texture_depth_multisampled_2d");
279
280 let array = crate::TypeInner::BindingArray {
281 base: mytype1,
282 size: crate::ArraySize::Constant(unsafe { NonZeroU32::new_unchecked(32) }),
283 };
284 assert_eq!(array.to_wgsl(gctx), "binding_array<MyType1, 32>");
285 }
286}
287
288impl crate::ScalarKind {
289 fn to_wgsl(self, width: u8) -> String {
293 let prefix = match self {
294 crate::ScalarKind::Sint => "i",
295 crate::ScalarKind::Uint => "u",
296 crate::ScalarKind::Float => "f",
297 crate::ScalarKind::Bool => return "bool".to_string(),
298 };
299 format!("{}{}", prefix, width * 8)
300 }
301}