Skip to main content

aver/types/
float.rs

1/// Float namespace — numeric helpers for floating-point values.
2///
3/// Methods:
4///   Float.fromString(s)  → Result<Float, String>  — parse string to float
5///   Float.fromInt(n)     → Float                  — widen int to float
6///   Float.toString(f)    → String                 — format float as string
7///   Float.abs(f)         → Float                  — absolute value
8///   Float.floor(f)       → Int                    — floor to int
9///   Float.ceil(f)        → Int                    — ceil to int
10///   Float.round(f)       → Int                    — round to int
11///   Float.min(a, b)      → Float                  — minimum of two floats
12///   Float.max(a, b)      → Float                  — maximum of two floats
13///
14/// No effects required.
15use 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
50/// Returns `Some(result)` when `name` is owned by this namespace, `None` otherwise.
51pub 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
66// ─── Implementations ────────────────────────────────────────────────────────
67
68fn 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
164// ─── Helpers ────────────────────────────────────────────────────────────────
165
166fn 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}