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