1use std::collections::HashMap;
21use std::sync::Arc as Rc;
22
23use crate::nan_value::{Arena, NanValue};
24use crate::value::{RuntimeError, Value};
25
26pub fn register(global: &mut HashMap<String, Value>) {
27 let mut members = HashMap::new();
28 for method in &[
29 "fromString",
30 "fromInt",
31 "abs",
32 "floor",
33 "ceil",
34 "round",
35 "min",
36 "max",
37 "sin",
38 "cos",
39 "sqrt",
40 "pow",
41 "atan2",
42 "pi",
43 ] {
44 members.insert(
45 method.to_string(),
46 Value::Builtin(format!("Float.{}", method)),
47 );
48 }
49 global.insert(
50 "Float".to_string(),
51 Value::Namespace {
52 name: "Float".to_string(),
53 members,
54 },
55 );
56}
57
58pub fn effects(_name: &str) -> &'static [&'static str] {
59 &[]
60}
61
62pub fn call(name: &str, args: &[Value]) -> Option<Result<Value, RuntimeError>> {
64 match name {
65 "Float.fromString" => Some(from_string(args)),
66 "Float.fromInt" => Some(from_int(args)),
67 "Float.abs" => Some(abs(args)),
68 "Float.floor" => Some(floor(args)),
69 "Float.ceil" => Some(ceil(args)),
70 "Float.round" => Some(round(args)),
71 "Float.min" => Some(min(args)),
72 "Float.max" => Some(max(args)),
73 "Float.sin" => Some(sin(args)),
74 "Float.cos" => Some(cos(args)),
75 "Float.sqrt" => Some(sqrt(args)),
76 "Float.pow" => Some(pow(args)),
77 "Float.atan2" => Some(atan2(args)),
78 "Float.pi" => Some(pi(args)),
79 _ => None,
80 }
81}
82
83fn from_string(args: &[Value]) -> Result<Value, RuntimeError> {
86 let [val] = one_arg("Float.fromString", args)?;
87 let Value::Str(s) = val else {
88 return Err(RuntimeError::Error(
89 "Float.fromString: argument must be a String".to_string(),
90 ));
91 };
92 match s.parse::<f64>() {
93 Ok(f) => Ok(Value::Ok(Box::new(Value::Float(f)))),
94 Err(_) => Ok(Value::Err(Box::new(Value::Str(format!(
95 "Cannot parse '{}' as Float",
96 s
97 ))))),
98 }
99}
100
101fn from_int(args: &[Value]) -> Result<Value, RuntimeError> {
102 let [val] = one_arg("Float.fromInt", args)?;
103 let Value::Int(n) = val else {
104 return Err(RuntimeError::Error(
105 "Float.fromInt: argument must be an Int".to_string(),
106 ));
107 };
108 Ok(Value::Float(*n as f64))
109}
110
111fn abs(args: &[Value]) -> Result<Value, RuntimeError> {
112 let [val] = one_arg("Float.abs", args)?;
113 let Value::Float(f) = val else {
114 return Err(RuntimeError::Error(
115 "Float.abs: argument must be a Float".to_string(),
116 ));
117 };
118 Ok(Value::Float(f.abs()))
119}
120
121fn floor(args: &[Value]) -> Result<Value, RuntimeError> {
122 let [val] = one_arg("Float.floor", args)?;
123 let Value::Float(f) = val else {
124 return Err(RuntimeError::Error(
125 "Float.floor: argument must be a Float".to_string(),
126 ));
127 };
128 Ok(Value::Int(f.floor() as i64))
129}
130
131fn ceil(args: &[Value]) -> Result<Value, RuntimeError> {
132 let [val] = one_arg("Float.ceil", args)?;
133 let Value::Float(f) = val else {
134 return Err(RuntimeError::Error(
135 "Float.ceil: argument must be a Float".to_string(),
136 ));
137 };
138 Ok(Value::Int(f.ceil() as i64))
139}
140
141fn round(args: &[Value]) -> Result<Value, RuntimeError> {
142 let [val] = one_arg("Float.round", args)?;
143 let Value::Float(f) = val else {
144 return Err(RuntimeError::Error(
145 "Float.round: argument must be a Float".to_string(),
146 ));
147 };
148 Ok(Value::Int(f.round() as i64))
149}
150
151fn min(args: &[Value]) -> Result<Value, RuntimeError> {
152 let [a, b] = two_args("Float.min", args)?;
153 let (Value::Float(x), Value::Float(y)) = (a, b) else {
154 return Err(RuntimeError::Error(
155 "Float.min: both arguments must be Float".to_string(),
156 ));
157 };
158 Ok(Value::Float(f64::min(*x, *y)))
159}
160
161fn max(args: &[Value]) -> Result<Value, RuntimeError> {
162 let [a, b] = two_args("Float.max", args)?;
163 let (Value::Float(x), Value::Float(y)) = (a, b) else {
164 return Err(RuntimeError::Error(
165 "Float.max: both arguments must be Float".to_string(),
166 ));
167 };
168 Ok(Value::Float(f64::max(*x, *y)))
169}
170
171fn sin(args: &[Value]) -> Result<Value, RuntimeError> {
172 let [val] = one_arg("Float.sin", args)?;
173 let Value::Float(f) = val else {
174 return Err(RuntimeError::Error(
175 "Float.sin: argument must be a Float".to_string(),
176 ));
177 };
178 Ok(Value::Float(f.sin()))
179}
180
181fn cos(args: &[Value]) -> Result<Value, RuntimeError> {
182 let [val] = one_arg("Float.cos", args)?;
183 let Value::Float(f) = val else {
184 return Err(RuntimeError::Error(
185 "Float.cos: argument must be a Float".to_string(),
186 ));
187 };
188 Ok(Value::Float(f.cos()))
189}
190
191fn sqrt(args: &[Value]) -> Result<Value, RuntimeError> {
192 let [val] = one_arg("Float.sqrt", args)?;
193 let Value::Float(f) = val else {
194 return Err(RuntimeError::Error(
195 "Float.sqrt: argument must be a Float".to_string(),
196 ));
197 };
198 Ok(Value::Float(f.sqrt()))
199}
200
201fn pow(args: &[Value]) -> Result<Value, RuntimeError> {
202 let [a, b] = two_args("Float.pow", args)?;
203 let (Value::Float(base), Value::Float(exp)) = (a, b) else {
204 return Err(RuntimeError::Error(
205 "Float.pow: both arguments must be Float".to_string(),
206 ));
207 };
208 Ok(Value::Float(base.powf(*exp)))
209}
210
211fn atan2(args: &[Value]) -> Result<Value, RuntimeError> {
212 let [a, b] = two_args("Float.atan2", args)?;
213 let (Value::Float(y), Value::Float(x)) = (a, b) else {
214 return Err(RuntimeError::Error(
215 "Float.atan2: both arguments must be Float".to_string(),
216 ));
217 };
218 Ok(Value::Float(y.atan2(*x)))
219}
220
221fn pi(args: &[Value]) -> Result<Value, RuntimeError> {
222 if !args.is_empty() {
223 return Err(RuntimeError::Error(format!(
224 "Float.pi() takes 0 arguments, got {}",
225 args.len()
226 )));
227 }
228 Ok(Value::Float(std::f64::consts::PI))
229}
230
231fn one_arg<'a>(name: &str, args: &'a [Value]) -> Result<[&'a Value; 1], RuntimeError> {
234 if args.len() != 1 {
235 return Err(RuntimeError::Error(format!(
236 "{}() takes 1 argument, got {}",
237 name,
238 args.len()
239 )));
240 }
241 Ok([&args[0]])
242}
243
244fn two_args<'a>(name: &str, args: &'a [Value]) -> Result<[&'a Value; 2], RuntimeError> {
245 if args.len() != 2 {
246 return Err(RuntimeError::Error(format!(
247 "{}() takes 2 arguments, got {}",
248 name,
249 args.len()
250 )));
251 }
252 Ok([&args[0], &args[1]])
253}
254
255pub fn register_nv(global: &mut HashMap<String, NanValue>, arena: &mut Arena) {
258 let methods = &[
259 "fromString",
260 "fromInt",
261 "abs",
262 "floor",
263 "ceil",
264 "round",
265 "min",
266 "max",
267 "sin",
268 "cos",
269 "sqrt",
270 "pow",
271 "atan2",
272 "pi",
273 ];
274 let mut members: Vec<(Rc<str>, NanValue)> = Vec::with_capacity(methods.len());
275 for method in methods {
276 let idx = arena.push_builtin(&format!("Float.{}", method));
277 members.push((Rc::from(*method), NanValue::new_builtin(idx)));
278 }
279 let ns_idx = arena.push(crate::nan_value::ArenaEntry::Namespace {
280 name: Rc::from("Float"),
281 members,
282 });
283 global.insert("Float".to_string(), NanValue::new_namespace(ns_idx));
284}
285
286pub fn call_nv(
287 name: &str,
288 args: &[NanValue],
289 arena: &mut Arena,
290) -> Option<Result<NanValue, RuntimeError>> {
291 match name {
292 "Float.fromString" => Some(from_string_nv(args, arena)),
293 "Float.fromInt" => Some(from_int_nv(args, arena)),
294 "Float.abs" => Some(abs_nv(args, arena)),
295 "Float.floor" => Some(floor_nv(args, arena)),
296 "Float.ceil" => Some(ceil_nv(args, arena)),
297 "Float.round" => Some(round_nv(args, arena)),
298 "Float.min" => Some(min_nv(args, arena)),
299 "Float.max" => Some(max_nv(args, arena)),
300 "Float.sin" => Some(sin_nv(args, arena)),
301 "Float.cos" => Some(cos_nv(args, arena)),
302 "Float.sqrt" => Some(sqrt_nv(args, arena)),
303 "Float.pow" => Some(pow_nv(args, arena)),
304 "Float.atan2" => Some(atan2_nv(args, arena)),
305 "Float.pi" => Some(pi_nv(args)),
306 _ => None,
307 }
308}
309
310fn nv_check1(name: &str, args: &[NanValue]) -> Result<NanValue, RuntimeError> {
311 if args.len() != 1 {
312 return Err(RuntimeError::Error(format!(
313 "{}() takes 1 argument, got {}",
314 name,
315 args.len()
316 )));
317 }
318 Ok(args[0])
319}
320
321fn nv_check2(name: &str, args: &[NanValue]) -> Result<(NanValue, NanValue), RuntimeError> {
322 if args.len() != 2 {
323 return Err(RuntimeError::Error(format!(
324 "{}() takes 2 arguments, got {}",
325 name,
326 args.len()
327 )));
328 }
329 Ok((args[0], args[1]))
330}
331
332fn from_string_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
333 let v = nv_check1("Float.fromString", args)?;
334 if !v.is_string() {
335 return Err(RuntimeError::Error(
336 "Float.fromString: argument must be a String".to_string(),
337 ));
338 }
339 let s = arena.get_string_value(v);
340 match s.parse::<f64>() {
341 Ok(f) => {
342 let inner = NanValue::new_float(f);
343 Ok(NanValue::new_ok_value(inner, arena))
344 }
345 Err(_) => {
346 let msg = format!("Cannot parse '{}' as Float", s);
347 let inner = NanValue::new_string_value(&msg, arena);
348 Ok(NanValue::new_err_value(inner, arena))
349 }
350 }
351}
352
353fn from_int_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
354 let v = nv_check1("Float.fromInt", args)?;
355 if !v.is_int() {
356 return Err(RuntimeError::Error(
357 "Float.fromInt: argument must be an Int".to_string(),
358 ));
359 }
360 Ok(NanValue::new_float(v.as_int(arena) as f64))
361}
362
363fn abs_nv(args: &[NanValue], _arena: &mut Arena) -> Result<NanValue, RuntimeError> {
364 let v = nv_check1("Float.abs", args)?;
365 if !v.is_float() {
366 return Err(RuntimeError::Error(
367 "Float.abs: argument must be a Float".to_string(),
368 ));
369 }
370 Ok(NanValue::new_float(v.as_float().abs()))
371}
372
373fn floor_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
374 let v = nv_check1("Float.floor", args)?;
375 if !v.is_float() {
376 return Err(RuntimeError::Error(
377 "Float.floor: argument must be a Float".to_string(),
378 ));
379 }
380 Ok(NanValue::new_int(v.as_float().floor() as i64, arena))
381}
382
383fn ceil_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
384 let v = nv_check1("Float.ceil", args)?;
385 if !v.is_float() {
386 return Err(RuntimeError::Error(
387 "Float.ceil: argument must be a Float".to_string(),
388 ));
389 }
390 Ok(NanValue::new_int(v.as_float().ceil() as i64, arena))
391}
392
393fn round_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
394 let v = nv_check1("Float.round", args)?;
395 if !v.is_float() {
396 return Err(RuntimeError::Error(
397 "Float.round: argument must be a Float".to_string(),
398 ));
399 }
400 Ok(NanValue::new_int(v.as_float().round() as i64, arena))
401}
402
403fn min_nv(args: &[NanValue], _arena: &mut Arena) -> Result<NanValue, RuntimeError> {
404 let (a, b) = nv_check2("Float.min", args)?;
405 if !a.is_float() || !b.is_float() {
406 return Err(RuntimeError::Error(
407 "Float.min: both arguments must be Float".to_string(),
408 ));
409 }
410 Ok(NanValue::new_float(f64::min(a.as_float(), b.as_float())))
411}
412
413fn max_nv(args: &[NanValue], _arena: &mut Arena) -> Result<NanValue, RuntimeError> {
414 let (a, b) = nv_check2("Float.max", args)?;
415 if !a.is_float() || !b.is_float() {
416 return Err(RuntimeError::Error(
417 "Float.max: both arguments must be Float".to_string(),
418 ));
419 }
420 Ok(NanValue::new_float(f64::max(a.as_float(), b.as_float())))
421}
422
423fn sin_nv(args: &[NanValue], _arena: &mut Arena) -> Result<NanValue, RuntimeError> {
424 let v = nv_check1("Float.sin", args)?;
425 if !v.is_float() {
426 return Err(RuntimeError::Error(
427 "Float.sin: argument must be a Float".to_string(),
428 ));
429 }
430 Ok(NanValue::new_float(v.as_float().sin()))
431}
432
433fn cos_nv(args: &[NanValue], _arena: &mut Arena) -> Result<NanValue, RuntimeError> {
434 let v = nv_check1("Float.cos", args)?;
435 if !v.is_float() {
436 return Err(RuntimeError::Error(
437 "Float.cos: argument must be a Float".to_string(),
438 ));
439 }
440 Ok(NanValue::new_float(v.as_float().cos()))
441}
442
443fn sqrt_nv(args: &[NanValue], _arena: &mut Arena) -> Result<NanValue, RuntimeError> {
444 let v = nv_check1("Float.sqrt", args)?;
445 if !v.is_float() {
446 return Err(RuntimeError::Error(
447 "Float.sqrt: argument must be a Float".to_string(),
448 ));
449 }
450 Ok(NanValue::new_float(v.as_float().sqrt()))
451}
452
453fn pow_nv(args: &[NanValue], _arena: &mut Arena) -> Result<NanValue, RuntimeError> {
454 let (a, b) = nv_check2("Float.pow", args)?;
455 if !a.is_float() || !b.is_float() {
456 return Err(RuntimeError::Error(
457 "Float.pow: both arguments must be Float".to_string(),
458 ));
459 }
460 Ok(NanValue::new_float(a.as_float().powf(b.as_float())))
461}
462
463fn atan2_nv(args: &[NanValue], _arena: &mut Arena) -> Result<NanValue, RuntimeError> {
464 let (a, b) = nv_check2("Float.atan2", args)?;
465 if !a.is_float() || !b.is_float() {
466 return Err(RuntimeError::Error(
467 "Float.atan2: both arguments must be Float".to_string(),
468 ));
469 }
470 Ok(NanValue::new_float(a.as_float().atan2(b.as_float())))
471}
472
473fn pi_nv(args: &[NanValue]) -> Result<NanValue, RuntimeError> {
474 if !args.is_empty() {
475 return Err(RuntimeError::Error(format!(
476 "Float.pi() takes 0 arguments, got {}",
477 args.len()
478 )));
479 }
480 Ok(NanValue::new_float(std::f64::consts::PI))
481}