1use std::collections::HashMap;
16
17use crate::value::{RuntimeError, Value};
18
19pub fn register(global: &mut HashMap<String, Value>) {
20 let mut members = HashMap::new();
21 for method in &[
22 "fromString",
23 "fromInt",
24 "toString",
25 "abs",
26 "floor",
27 "ceil",
28 "round",
29 "min",
30 "max",
31 ] {
32 members.insert(
33 method.to_string(),
34 Value::Builtin(format!("Float.{}", method)),
35 );
36 }
37 global.insert(
38 "Float".to_string(),
39 Value::Namespace {
40 name: "Float".to_string(),
41 members,
42 },
43 );
44}
45
46pub fn effects(_name: &str) -> &'static [&'static str] {
47 &[]
48}
49
50pub fn call(name: &str, args: &[Value]) -> Option<Result<Value, RuntimeError>> {
52 match name {
53 "Float.fromString" => Some(from_string(args)),
54 "Float.fromInt" => Some(from_int(args)),
55 "Float.toString" => Some(to_string(args)),
56 "Float.abs" => Some(abs(args)),
57 "Float.floor" => Some(floor(args)),
58 "Float.ceil" => Some(ceil(args)),
59 "Float.round" => Some(round(args)),
60 "Float.min" => Some(min(args)),
61 "Float.max" => Some(max(args)),
62 _ => None,
63 }
64}
65
66fn from_string(args: &[Value]) -> Result<Value, RuntimeError> {
69 let [val] = one_arg("Float.fromString", args)?;
70 let Value::Str(s) = val else {
71 return Err(RuntimeError::Error(
72 "Float.fromString: argument must be a String".to_string(),
73 ));
74 };
75 match s.parse::<f64>() {
76 Ok(f) => Ok(Value::Ok(Box::new(Value::Float(f)))),
77 Err(_) => Ok(Value::Err(Box::new(Value::Str(format!(
78 "Cannot parse '{}' as Float",
79 s
80 ))))),
81 }
82}
83
84fn from_int(args: &[Value]) -> Result<Value, RuntimeError> {
85 let [val] = one_arg("Float.fromInt", args)?;
86 let Value::Int(n) = val else {
87 return Err(RuntimeError::Error(
88 "Float.fromInt: argument must be an Int".to_string(),
89 ));
90 };
91 Ok(Value::Float(*n as f64))
92}
93
94fn to_string(args: &[Value]) -> Result<Value, RuntimeError> {
95 let [val] = one_arg("Float.toString", args)?;
96 let Value::Float(f) = val else {
97 return Err(RuntimeError::Error(
98 "Float.toString: argument must be a Float".to_string(),
99 ));
100 };
101 Ok(Value::Str(format!("{}", f)))
102}
103
104fn abs(args: &[Value]) -> Result<Value, RuntimeError> {
105 let [val] = one_arg("Float.abs", args)?;
106 let Value::Float(f) = val else {
107 return Err(RuntimeError::Error(
108 "Float.abs: argument must be a Float".to_string(),
109 ));
110 };
111 Ok(Value::Float(f.abs()))
112}
113
114fn floor(args: &[Value]) -> Result<Value, RuntimeError> {
115 let [val] = one_arg("Float.floor", args)?;
116 let Value::Float(f) = val else {
117 return Err(RuntimeError::Error(
118 "Float.floor: argument must be a Float".to_string(),
119 ));
120 };
121 Ok(Value::Int(f.floor() as i64))
122}
123
124fn ceil(args: &[Value]) -> Result<Value, RuntimeError> {
125 let [val] = one_arg("Float.ceil", args)?;
126 let Value::Float(f) = val else {
127 return Err(RuntimeError::Error(
128 "Float.ceil: argument must be a Float".to_string(),
129 ));
130 };
131 Ok(Value::Int(f.ceil() as i64))
132}
133
134fn round(args: &[Value]) -> Result<Value, RuntimeError> {
135 let [val] = one_arg("Float.round", args)?;
136 let Value::Float(f) = val else {
137 return Err(RuntimeError::Error(
138 "Float.round: argument must be a Float".to_string(),
139 ));
140 };
141 Ok(Value::Int(f.round() as i64))
142}
143
144fn min(args: &[Value]) -> Result<Value, RuntimeError> {
145 let [a, b] = two_args("Float.min", args)?;
146 let (Value::Float(x), Value::Float(y)) = (a, b) else {
147 return Err(RuntimeError::Error(
148 "Float.min: both arguments must be Float".to_string(),
149 ));
150 };
151 Ok(Value::Float(f64::min(*x, *y)))
152}
153
154fn max(args: &[Value]) -> Result<Value, RuntimeError> {
155 let [a, b] = two_args("Float.max", args)?;
156 let (Value::Float(x), Value::Float(y)) = (a, b) else {
157 return Err(RuntimeError::Error(
158 "Float.max: both arguments must be Float".to_string(),
159 ));
160 };
161 Ok(Value::Float(f64::max(*x, *y)))
162}
163
164fn one_arg<'a>(name: &str, args: &'a [Value]) -> Result<[&'a Value; 1], RuntimeError> {
167 if args.len() != 1 {
168 return Err(RuntimeError::Error(format!(
169 "{}() takes 1 argument, got {}",
170 name,
171 args.len()
172 )));
173 }
174 Ok([&args[0]])
175}
176
177fn two_args<'a>(name: &str, args: &'a [Value]) -> Result<[&'a Value; 2], RuntimeError> {
178 if args.len() != 2 {
179 return Err(RuntimeError::Error(format!(
180 "{}() takes 2 arguments, got {}",
181 name,
182 args.len()
183 )));
184 }
185 Ok([&args[0], &args[1]])
186}