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