1use crate::Type;
8
9#[derive(Clone, Copy, Debug, Eq, PartialEq)]
11pub enum Operation {
12 Len,
14 Utf8,
16 Decode,
18 Append,
20 Get,
22 Shape,
24 Reshape,
26 Transpose,
28 Sum,
30 Matmul,
32 Convert,
34 Numeric(&'static str),
36 Mesh,
38}
39
40pub fn operation(name: &str) -> Option<Operation> {
42 Some(match name {
43 "len" => Operation::Len,
44 "utf8" => Operation::Utf8,
45 "decode" => Operation::Decode,
46 "append" => Operation::Append,
47 "get" => Operation::Get,
48 "shape" => Operation::Shape,
49 "reshape" => Operation::Reshape,
50 "transpose" => Operation::Transpose,
51 "sum" => Operation::Sum,
52 "matmul" => Operation::Matmul,
53 "convert" => Operation::Convert,
54 "sin" => Operation::Numeric("sin"),
55 "cos" => Operation::Numeric("cos"),
56 "sqrt" => Operation::Numeric("sqrt"),
57 "abs" => Operation::Numeric("abs"),
58 "floor" => Operation::Numeric("floor"),
59 "ceil" => Operation::Numeric("ceil"),
60 "min" => Operation::Numeric("min"),
61 "max" => Operation::Numeric("max"),
62 "clamp" => Operation::Numeric("clamp"),
63 "pow" => Operation::Numeric("pow"),
64 "sphere" | "torus" | "cone" | "grid" | "lathe" | "suzanne" | "translated" | "scaled"
65 | "rotated" | "mapped" | "decode_json" => Operation::Mesh,
66 _ => return None,
67 })
68}
69
70pub fn is_owned_global(name: &str) -> bool {
72 matches!(
73 name,
74 "len"
75 | "decode"
76 | "utf8"
77 | "shape"
78 | "reshape"
79 | "transpose"
80 | "sum"
81 | "convert"
82 | "matmul"
83 | "append"
84 | "sin"
85 | "cos"
86 | "sqrt"
87 | "abs"
88 | "floor"
89 | "ceil"
90 | "min"
91 | "max"
92 | "clamp"
93 | "pow"
94 | "sphere"
95 | "torus"
96 | "cone"
97 | "grid"
98 | "lathe"
99 | "suzanne"
100 | "translated"
101 | "scaled"
102 | "rotated"
103 | "mapped"
104 | "decode_json"
105 )
106}
107
108pub fn signatures(receiver: &Type, name: &str, type_arg: Option<&Type>) -> Vec<Type> {
113 let number = Type::named("f64");
114 let text = Type::named("Str");
115 let binary = Type::named("Bin");
116 let error = Type::named("DataError");
117 let list_number = Type::List(Box::new(number.clone()));
118 let result = |value| applied("Res", vec![value, error.clone()]);
119 let option = |value| applied("Opt", vec![value]);
120
121 match receiver {
122 Type::Named(value) if value == "Str" => match name {
123 "len" => vec![Type::function(vec![], number)],
124 "utf8" => vec![Type::function(vec![], binary)],
125 "get" => vec![Type::function(vec![Type::named("f64")], option(text))],
126 _ => vec![],
127 },
128 Type::Named(value) if value == "Bin" => match name {
129 "len" => vec![Type::function(vec![], number)],
130 "decode" => vec![Type::function(vec![], result(text))],
131 "get" => vec![Type::function(
132 vec![Type::named("f64")],
133 option(Type::named("u8")),
134 )],
135 _ => vec![],
136 },
137 Type::List(element) => match name {
138 "len" => vec![Type::function(vec![], number)],
139 "append" => vec![Type::function(
140 vec![element.as_ref().clone()],
141 Type::List(element.clone()),
142 )],
143 "get" => vec![Type::function(
144 vec![Type::named("f64")],
145 option(element.as_ref().clone()),
146 )],
147 _ => vec![],
148 },
149 Type::Tensor(element) => match name {
150 "len" => vec![Type::function(vec![], number)],
151 "shape" => vec![Type::function(vec![], list_number.clone())],
152 "reshape" => vec![Type::function(
153 vec![list_number.clone()],
154 result(Type::Tensor(element.clone())),
155 )],
156 "transpose" => vec![
157 Type::function(vec![], result(Type::Tensor(element.clone()))),
158 Type::function(
159 vec![list_number.clone()],
160 result(Type::Tensor(element.clone())),
161 ),
162 ],
163 "sum" => vec![Type::function(vec![], result(element.as_ref().clone()))],
164 "matmul" => vec![Type::function(
165 vec![Type::Tensor(element.clone())],
166 result(Type::Tensor(element.clone())),
167 )],
168 "get" => vec![Type::function(
169 vec![list_number],
170 option(element.as_ref().clone()),
171 )],
172 "convert" if is_dtype(type_arg) => vec![Type::function(
173 vec![],
174 result(Type::Tensor(Box::new(
175 type_arg.expect("checked above").clone(),
176 ))),
177 )],
178 _ => vec![],
179 },
180 Type::Named(value) if value == "f64" => match name {
181 "sin" | "cos" | "sqrt" | "abs" | "floor" | "ceil" => {
182 vec![Type::function(vec![], result(number.clone()))]
183 }
184 "min" | "max" | "pow" => vec![Type::function(vec![number.clone()], result(number))],
185 "clamp" => vec![Type::function(
186 vec![number.clone(), number.clone()],
187 result(number),
188 )],
189 "convert" if is_dtype(type_arg) => vec![Type::function(
190 vec![],
191 result(type_arg.expect("checked above").clone()),
192 )],
193 _ => vec![],
194 },
195 Type::Named(value) if value == "Mesh" => {
196 let mesh = Type::named("Mesh");
197 let vec3 = Type::named("Vec3");
198 let number = Type::named("f64");
199 let mesh_result = result(mesh.clone());
200 match name {
201 "sphere" => vec![Type::function(
202 vec![number.clone(), number.clone(), number.clone()],
203 mesh_result,
204 )],
205 "torus" => vec![Type::function(
206 vec![
207 number.clone(),
208 number.clone(),
209 number.clone(),
210 number.clone(),
211 ],
212 mesh_result,
213 )],
214 "cone" => vec![Type::function(
215 vec![number.clone(), number.clone(), number.clone()],
216 mesh_result,
217 )],
218 "grid" => vec![Type::function(
219 vec![
220 number.clone(),
221 number.clone(),
222 number.clone(),
223 number.clone(),
224 ],
225 mesh_result,
226 )],
227 "lathe" => vec![Type::function(
228 vec![Type::Tensor(Box::new(Type::named("f32"))), number.clone()],
229 mesh_result,
230 )],
231 "suzanne" => vec![Type::function(vec![], mesh_result)],
232 "translated" | "scaled" | "rotated" => {
233 vec![Type::function(vec![vec3], mesh_result)]
234 }
235 "mapped" => vec![Type::function(
236 vec![Type::function(vec![vec3], result(Type::named("Vec3")))],
237 mesh_result,
238 )],
239 "decode_json" => vec![Type::function(
240 vec![Type::List(Box::new(Type::named("Str")))],
241 mesh_result,
242 )],
243 _ => vec![],
244 }
245 }
246 Type::Named(_) if is_dtype(Some(receiver)) && name == "convert" && is_dtype(type_arg) => {
247 vec![Type::function(
248 vec![],
249 result(type_arg.expect("checked above").clone()),
250 )]
251 }
252 _ => vec![],
253 }
254}
255
256pub fn signature(receiver: &Type, name: &str, type_arg: Option<&Type>) -> Option<Type> {
260 let mut signatures = signatures(receiver, name, type_arg).into_iter();
261 let signature = signatures.next()?;
262 signatures.next().is_none().then_some(signature)
263}
264
265fn applied(name: &str, arguments: Vec<Type>) -> Type {
266 Type::Applied {
267 name: name.into(),
268 arguments,
269 }
270}
271
272fn is_dtype(ty: Option<&Type>) -> bool {
273 ty.is_some_and(|ty| crate::data::dtype(ty).is_some())
274}
275
276#[cfg(test)]
277mod tests {
278 use super::*;
279
280 #[test]
281 fn data_methods_have_receiver_specialized_results() {
282 let tensor = Type::Tensor(Box::new(Type::named("f32")));
283 assert_eq!(
284 signature(&Type::named("Bin"), "decode", None),
285 Some(Type::function(
286 vec![],
287 Type::Applied {
288 name: "Res".into(),
289 arguments: vec![Type::named("Str"), Type::named("DataError")],
290 },
291 ))
292 );
293 assert_eq!(
294 signature(&Type::List(Box::new(Type::named("u8"))), "append", None),
295 Some(Type::function(
296 vec![Type::named("u8")],
297 Type::List(Box::new(Type::named("u8"))),
298 ))
299 );
300 assert_eq!(
301 signature(&tensor, "convert", Some(&Type::named("u16"))),
302 Some(Type::function(
303 vec![],
304 Type::Applied {
305 name: "Res".into(),
306 arguments: vec![
307 Type::Tensor(Box::new(Type::named("u16"))),
308 Type::named("DataError"),
309 ],
310 },
311 ))
312 );
313 }
314
315 #[test]
316 fn transpose_keeps_its_two_arity_overloads_explicit() {
317 let tensor = Type::Tensor(Box::new(Type::named("f64")));
318 assert_eq!(signature(&tensor, "transpose", None), None);
319 assert_eq!(signatures(&tensor, "transpose", None).len(), 2);
320 }
321
322 #[test]
323 fn owned_globals_are_not_left_in_the_function_namespace() {
324 for name in [
325 "len",
326 "decode",
327 "utf8",
328 "shape",
329 "reshape",
330 "transpose",
331 "sum",
332 "convert",
333 "matmul",
334 "append",
335 ] {
336 assert!(is_owned_global(name));
337 assert!(operation(name).is_some());
338 }
339 assert!(!is_owned_global("bytes"));
340 assert_eq!(operation("bytes"), None);
341 }
342}