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