1use std::collections::HashMap;
16use std::rc::Rc;
17
18use crate::nan_value::{Arena, NanValue};
19use crate::value::{RuntimeError, Value};
20
21pub fn register(global: &mut HashMap<String, Value>) {
22 let mut members = HashMap::new();
23 for method in &[
24 "fromString",
25 "fromInt",
26 "toString",
27 "abs",
28 "floor",
29 "ceil",
30 "round",
31 "min",
32 "max",
33 ] {
34 members.insert(
35 method.to_string(),
36 Value::Builtin(format!("Float.{}", method)),
37 );
38 }
39 global.insert(
40 "Float".to_string(),
41 Value::Namespace {
42 name: "Float".to_string(),
43 members,
44 },
45 );
46}
47
48pub fn effects(_name: &str) -> &'static [&'static str] {
49 &[]
50}
51
52pub fn call(name: &str, args: &[Value]) -> Option<Result<Value, RuntimeError>> {
54 match name {
55 "Float.fromString" => Some(from_string(args)),
56 "Float.fromInt" => Some(from_int(args)),
57 "Float.toString" => Some(to_string(args)),
58 "Float.abs" => Some(abs(args)),
59 "Float.floor" => Some(floor(args)),
60 "Float.ceil" => Some(ceil(args)),
61 "Float.round" => Some(round(args)),
62 "Float.min" => Some(min(args)),
63 "Float.max" => Some(max(args)),
64 _ => None,
65 }
66}
67
68fn from_string(args: &[Value]) -> Result<Value, RuntimeError> {
71 let [val] = one_arg("Float.fromString", args)?;
72 let Value::Str(s) = val else {
73 return Err(RuntimeError::Error(
74 "Float.fromString: argument must be a String".to_string(),
75 ));
76 };
77 match s.parse::<f64>() {
78 Ok(f) => Ok(Value::Ok(Box::new(Value::Float(f)))),
79 Err(_) => Ok(Value::Err(Box::new(Value::Str(format!(
80 "Cannot parse '{}' as Float",
81 s
82 ))))),
83 }
84}
85
86fn from_int(args: &[Value]) -> Result<Value, RuntimeError> {
87 let [val] = one_arg("Float.fromInt", args)?;
88 let Value::Int(n) = val else {
89 return Err(RuntimeError::Error(
90 "Float.fromInt: argument must be an Int".to_string(),
91 ));
92 };
93 Ok(Value::Float(*n as f64))
94}
95
96fn to_string(args: &[Value]) -> Result<Value, RuntimeError> {
97 let [val] = one_arg("Float.toString", args)?;
98 let Value::Float(f) = val else {
99 return Err(RuntimeError::Error(
100 "Float.toString: argument must be a Float".to_string(),
101 ));
102 };
103 Ok(Value::Str(format!("{}", f)))
104}
105
106fn abs(args: &[Value]) -> Result<Value, RuntimeError> {
107 let [val] = one_arg("Float.abs", args)?;
108 let Value::Float(f) = val else {
109 return Err(RuntimeError::Error(
110 "Float.abs: argument must be a Float".to_string(),
111 ));
112 };
113 Ok(Value::Float(f.abs()))
114}
115
116fn floor(args: &[Value]) -> Result<Value, RuntimeError> {
117 let [val] = one_arg("Float.floor", args)?;
118 let Value::Float(f) = val else {
119 return Err(RuntimeError::Error(
120 "Float.floor: argument must be a Float".to_string(),
121 ));
122 };
123 Ok(Value::Int(f.floor() as i64))
124}
125
126fn ceil(args: &[Value]) -> Result<Value, RuntimeError> {
127 let [val] = one_arg("Float.ceil", args)?;
128 let Value::Float(f) = val else {
129 return Err(RuntimeError::Error(
130 "Float.ceil: argument must be a Float".to_string(),
131 ));
132 };
133 Ok(Value::Int(f.ceil() as i64))
134}
135
136fn round(args: &[Value]) -> Result<Value, RuntimeError> {
137 let [val] = one_arg("Float.round", args)?;
138 let Value::Float(f) = val else {
139 return Err(RuntimeError::Error(
140 "Float.round: argument must be a Float".to_string(),
141 ));
142 };
143 Ok(Value::Int(f.round() as i64))
144}
145
146fn min(args: &[Value]) -> Result<Value, RuntimeError> {
147 let [a, b] = two_args("Float.min", args)?;
148 let (Value::Float(x), Value::Float(y)) = (a, b) else {
149 return Err(RuntimeError::Error(
150 "Float.min: both arguments must be Float".to_string(),
151 ));
152 };
153 Ok(Value::Float(f64::min(*x, *y)))
154}
155
156fn max(args: &[Value]) -> Result<Value, RuntimeError> {
157 let [a, b] = two_args("Float.max", args)?;
158 let (Value::Float(x), Value::Float(y)) = (a, b) else {
159 return Err(RuntimeError::Error(
160 "Float.max: both arguments must be Float".to_string(),
161 ));
162 };
163 Ok(Value::Float(f64::max(*x, *y)))
164}
165
166fn one_arg<'a>(name: &str, args: &'a [Value]) -> Result<[&'a Value; 1], RuntimeError> {
169 if args.len() != 1 {
170 return Err(RuntimeError::Error(format!(
171 "{}() takes 1 argument, got {}",
172 name,
173 args.len()
174 )));
175 }
176 Ok([&args[0]])
177}
178
179fn two_args<'a>(name: &str, args: &'a [Value]) -> Result<[&'a Value; 2], RuntimeError> {
180 if args.len() != 2 {
181 return Err(RuntimeError::Error(format!(
182 "{}() takes 2 arguments, got {}",
183 name,
184 args.len()
185 )));
186 }
187 Ok([&args[0], &args[1]])
188}
189
190pub fn register_nv(global: &mut HashMap<String, NanValue>, arena: &mut Arena) {
193 let methods = &[
194 "fromString",
195 "fromInt",
196 "toString",
197 "abs",
198 "floor",
199 "ceil",
200 "round",
201 "min",
202 "max",
203 ];
204 let mut members: Vec<(Rc<str>, NanValue)> = Vec::with_capacity(methods.len());
205 for method in methods {
206 let idx = arena.push_builtin(&format!("Float.{}", method));
207 members.push((Rc::from(*method), NanValue::new_builtin(idx)));
208 }
209 let ns_idx = arena.push(crate::nan_value::ArenaEntry::Namespace {
210 name: Rc::from("Float"),
211 members,
212 });
213 global.insert("Float".to_string(), NanValue::new_namespace(ns_idx));
214}
215
216pub fn call_nv(
217 name: &str,
218 args: &[NanValue],
219 arena: &mut Arena,
220) -> Option<Result<NanValue, RuntimeError>> {
221 match name {
222 "Float.fromString" => Some(from_string_nv(args, arena)),
223 "Float.fromInt" => Some(from_int_nv(args, arena)),
224 "Float.toString" => Some(to_string_nv(args, arena)),
225 "Float.abs" => Some(abs_nv(args, arena)),
226 "Float.floor" => Some(floor_nv(args, arena)),
227 "Float.ceil" => Some(ceil_nv(args, arena)),
228 "Float.round" => Some(round_nv(args, arena)),
229 "Float.min" => Some(min_nv(args, arena)),
230 "Float.max" => Some(max_nv(args, arena)),
231 _ => None,
232 }
233}
234
235fn nv_check1(name: &str, args: &[NanValue]) -> Result<NanValue, 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 nv_check2(name: &str, args: &[NanValue]) -> Result<(NanValue, NanValue), 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
257fn from_string_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
258 let v = nv_check1("Float.fromString", args)?;
259 if !v.is_string() {
260 return Err(RuntimeError::Error(
261 "Float.fromString: argument must be a String".to_string(),
262 ));
263 }
264 let s = arena.get_string_value(v);
265 match s.parse::<f64>() {
266 Ok(f) => {
267 let inner = NanValue::new_float(f);
268 Ok(NanValue::new_ok_value(inner, arena))
269 }
270 Err(_) => {
271 let msg = format!("Cannot parse '{}' as Float", s);
272 let inner = NanValue::new_string_value(&msg, arena);
273 Ok(NanValue::new_err_value(inner, arena))
274 }
275 }
276}
277
278fn from_int_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
279 let v = nv_check1("Float.fromInt", args)?;
280 if !v.is_int() {
281 return Err(RuntimeError::Error(
282 "Float.fromInt: argument must be an Int".to_string(),
283 ));
284 }
285 Ok(NanValue::new_float(v.as_int(arena) as f64))
286}
287
288fn to_string_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
289 let v = nv_check1("Float.toString", args)?;
290 if !v.is_float() {
291 return Err(RuntimeError::Error(
292 "Float.toString: argument must be a Float".to_string(),
293 ));
294 }
295 let s = format!("{}", v.as_float());
296 Ok(NanValue::new_string_value(&s, arena))
297}
298
299fn abs_nv(args: &[NanValue], _arena: &mut Arena) -> Result<NanValue, RuntimeError> {
300 let v = nv_check1("Float.abs", args)?;
301 if !v.is_float() {
302 return Err(RuntimeError::Error(
303 "Float.abs: argument must be a Float".to_string(),
304 ));
305 }
306 Ok(NanValue::new_float(v.as_float().abs()))
307}
308
309fn floor_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
310 let v = nv_check1("Float.floor", args)?;
311 if !v.is_float() {
312 return Err(RuntimeError::Error(
313 "Float.floor: argument must be a Float".to_string(),
314 ));
315 }
316 Ok(NanValue::new_int(v.as_float().floor() as i64, arena))
317}
318
319fn ceil_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
320 let v = nv_check1("Float.ceil", args)?;
321 if !v.is_float() {
322 return Err(RuntimeError::Error(
323 "Float.ceil: argument must be a Float".to_string(),
324 ));
325 }
326 Ok(NanValue::new_int(v.as_float().ceil() as i64, arena))
327}
328
329fn round_nv(args: &[NanValue], arena: &mut Arena) -> Result<NanValue, RuntimeError> {
330 let v = nv_check1("Float.round", args)?;
331 if !v.is_float() {
332 return Err(RuntimeError::Error(
333 "Float.round: argument must be a Float".to_string(),
334 ));
335 }
336 Ok(NanValue::new_int(v.as_float().round() as i64, arena))
337}
338
339fn min_nv(args: &[NanValue], _arena: &mut Arena) -> Result<NanValue, RuntimeError> {
340 let (a, b) = nv_check2("Float.min", args)?;
341 if !a.is_float() || !b.is_float() {
342 return Err(RuntimeError::Error(
343 "Float.min: both arguments must be Float".to_string(),
344 ));
345 }
346 Ok(NanValue::new_float(f64::min(a.as_float(), b.as_float())))
347}
348
349fn max_nv(args: &[NanValue], _arena: &mut Arena) -> Result<NanValue, RuntimeError> {
350 let (a, b) = nv_check2("Float.max", args)?;
351 if !a.is_float() || !b.is_float() {
352 return Err(RuntimeError::Error(
353 "Float.max: both arguments must be Float".to_string(),
354 ));
355 }
356 Ok(NanValue::new_float(f64::max(a.as_float(), b.as_float())))
357}