jetcrab\runtime/
builtins.rs1use crate::runtime::context::Context;
2use crate::vm::value::Value;
3use crate::vm::heap::HeapEntry;
4use std::collections::HashMap;
5
6pub type BuiltinFunction = fn(&mut Context, &[Value]) -> Result<Value, String>;
7
8pub struct Builtins {
9 functions: HashMap<String, BuiltinFunction>,
10}
11
12impl Builtins {
13 pub fn new() -> Self {
14 let mut builtins = Self {
15 functions: HashMap::new(),
16 };
17
18 builtins.register_math_functions();
19 builtins.register_string_functions();
20 builtins.register_array_functions();
21
22 builtins
23 }
24
25 fn register_math_functions(&mut self) {
26 self.functions.insert("Math.pow".to_string(), Self::math_pow);
27 self.functions.insert("Math.abs".to_string(), Self::math_abs);
28 self.functions.insert("Math.sqrt".to_string(), Self::math_sqrt);
29 self.functions.insert("Math.floor".to_string(), Self::math_floor);
30 self.functions.insert("Math.ceil".to_string(), Self::math_ceil);
31 self.functions.insert("Math.round".to_string(), Self::math_round);
32 self.functions.insert("Math.max".to_string(), Self::math_max);
33 self.functions.insert("Math.min".to_string(), Self::math_min);
34 }
35
36 fn register_string_functions(&mut self) {
37 self.functions.insert("String.prototype.toUpperCase".to_string(), Self::string_to_upper);
38 self.functions.insert("String.prototype.toLowerCase".to_string(), Self::string_to_lower);
39 self.functions.insert("String.prototype.trim".to_string(), Self::string_trim);
40 self.functions.insert("String.prototype.length".to_string(), Self::string_length);
41
42 self.functions.insert("toUpperCase".to_string(), Self::string_to_upper);
44 self.functions.insert("toLowerCase".to_string(), Self::string_to_lower);
45 self.functions.insert("trim".to_string(), Self::string_trim);
46 }
47
48 fn register_array_functions(&mut self) {
49 self.functions.insert("Array.prototype.push".to_string(), Self::array_push);
50 self.functions.insert("Array.prototype.pop".to_string(), Self::array_pop);
51 self.functions.insert("Array.prototype.length".to_string(), Self::array_length);
52 }
53
54 pub fn get_function(&self, name: &str) -> Option<&BuiltinFunction> {
55 self.functions.get(name)
56 }
57
58 fn math_pow(_context: &mut Context, args: &[Value]) -> Result<Value, String> {
60 if args.len() != 2 {
61 return Err("Math.pow requires exactly 2 arguments".to_string());
62 }
63
64 let base = match &args[0] {
65 Value::Number(n) => *n,
66 _ => return Err("First argument must be a number".to_string()),
67 };
68
69 let exponent = match &args[1] {
70 Value::Number(n) => *n,
71 _ => return Err("Second argument must be a number".to_string()),
72 };
73
74 Ok(Value::Number(base.powf(exponent)))
75 }
76
77 fn math_abs(_context: &mut Context, args: &[Value]) -> Result<Value, String> {
78 if args.len() != 1 {
79 return Err("Math.abs requires exactly 1 argument".to_string());
80 }
81
82 let num = match &args[0] {
83 Value::Number(n) => *n,
84 _ => return Err("Argument must be a number".to_string()),
85 };
86
87 Ok(Value::Number(num.abs()))
88 }
89
90 fn math_sqrt(_context: &mut Context, args: &[Value]) -> Result<Value, String> {
91 if args.len() != 1 {
92 return Err("Math.sqrt requires exactly 1 argument".to_string());
93 }
94
95 let num = match &args[0] {
96 Value::Number(n) => *n,
97 _ => return Err("Argument must be a number".to_string()),
98 };
99
100 Ok(Value::Number(num.sqrt()))
101 }
102
103 fn math_floor(_context: &mut Context, args: &[Value]) -> Result<Value, String> {
104 if args.len() != 1 {
105 return Err("Math.floor requires exactly 1 argument".to_string());
106 }
107
108 let num = match &args[0] {
109 Value::Number(n) => *n,
110 _ => return Err("Argument must be a number".to_string()),
111 };
112
113 Ok(Value::Number(num.floor()))
114 }
115
116 fn math_ceil(_context: &mut Context, args: &[Value]) -> Result<Value, String> {
117 if args.len() != 1 {
118 return Err("Math.ceil requires exactly 1 argument".to_string());
119 }
120
121 let num = match &args[0] {
122 Value::Number(n) => *n,
123 _ => return Err("Argument must be a number".to_string()),
124 };
125
126 Ok(Value::Number(num.ceil()))
127 }
128
129 fn math_round(_context: &mut Context, args: &[Value]) -> Result<Value, String> {
130 if args.len() != 1 {
131 return Err("Math.round requires exactly 1 argument".to_string());
132 }
133
134 let num = match &args[0] {
135 Value::Number(n) => *n,
136 _ => return Err("Argument must be a number".to_string()),
137 };
138
139 Ok(Value::Number(num.round()))
140 }
141
142 fn math_max(_context: &mut Context, args: &[Value]) -> Result<Value, String> {
143 if args.is_empty() {
144 return Err("Math.max requires at least 1 argument".to_string());
145 }
146
147 let mut max_val = f64::NEG_INFINITY;
148 for arg in args {
149 if let Value::Number(n) = arg {
150 if *n > max_val {
151 max_val = *n;
152 }
153 }
154 }
155
156 Ok(Value::Number(max_val))
157 }
158
159 fn math_min(_context: &mut Context, args: &[Value]) -> Result<Value, String> {
160 if args.is_empty() {
161 return Err("Math.min requires at least 1 argument".to_string());
162 }
163
164 let mut min_val = f64::INFINITY;
165 for arg in args {
166 if let Value::Number(n) = arg {
167 if *n < min_val {
168 min_val = *n;
169 }
170 }
171 }
172
173 Ok(Value::Number(min_val))
174 }
175
176 fn string_to_upper(_context: &mut Context, args: &[Value]) -> Result<Value, String> {
178 if args.len() != 1 {
179 return Err("toUpperCase requires exactly 1 argument".to_string());
180 }
181
182 let str_val = match &args[0] {
183 Value::String(s) => s,
184 _ => return Err("Argument must be a string".to_string()),
185 };
186
187 Ok(Value::String(str_val.to_uppercase()))
188 }
189
190 fn string_to_lower(_context: &mut Context, args: &[Value]) -> Result<Value, String> {
191 if args.len() != 1 {
192 return Err("toLowerCase requires exactly 1 argument".to_string());
193 }
194
195 let str_val = match &args[0] {
196 Value::String(s) => s,
197 _ => return Err("Argument must be a string".to_string()),
198 };
199
200 Ok(Value::String(str_val.to_lowercase()))
201 }
202
203 fn string_trim(_context: &mut Context, args: &[Value]) -> Result<Value, String> {
204 if args.len() != 1 {
205 return Err("trim requires exactly 1 argument".to_string());
206 }
207
208 let str_val = match &args[0] {
209 Value::String(s) => s,
210 _ => return Err("Argument must be a string".to_string()),
211 };
212
213 Ok(Value::String(str_val.trim().to_string()))
214 }
215
216 fn string_length(_context: &mut Context, args: &[Value]) -> Result<Value, String> {
217 if args.len() != 1 {
218 return Err("length requires exactly 1 argument".to_string());
219 }
220
221 let str_val = match &args[0] {
222 Value::String(s) => s,
223 _ => return Err("Argument must be a string".to_string()),
224 };
225
226 Ok(Value::Number(str_val.len() as f64))
227 }
228
229 fn array_push(_context: &mut Context, args: &[Value]) -> Result<Value, String> {
231 if args.len() < 2 {
232 return Err("push requires at least 2 arguments".to_string());
233 }
234
235 let array_handle = match &args[0] {
236 Value::Array(handle) => handle,
237 _ => return Err("First argument must be an array".to_string()),
238 };
239
240 if let Some(heap) = _context.get_heap() {
242 if let Some(HeapEntry::Array(arr)) = heap.get_mut(array_handle.id()) {
243 for arg in &args[1..] {
245 arr.push(arg.clone());
246 }
247
248 Ok(Value::Number(arr.len() as f64))
250 } else {
251 Err("Array not found in heap".to_string())
252 }
253 } else {
254 Err("Heap not available in context".to_string())
255 }
256 }
257
258 fn array_pop(_context: &mut Context, args: &[Value]) -> Result<Value, String> {
259 if args.len() != 1 {
260 return Err("pop requires exactly 1 argument".to_string());
261 }
262
263 let array_handle = match &args[0] {
264 Value::Array(handle) => handle,
265 _ => return Err("Argument must be an array".to_string()),
266 };
267
268 if let Some(heap) = _context.get_heap() {
270 if let Some(HeapEntry::Array(arr)) = heap.get_mut(array_handle.id()) {
271 if arr.is_empty() {
272 Ok(Value::Undefined)
273 } else {
274 arr.pop().ok_or("Failed to pop from array".to_string())
275 }
276 } else {
277 Err("Array not found in heap".to_string())
278 }
279 } else {
280 Err("Heap not available in context".to_string())
281 }
282 }
283
284 fn array_length(_context: &mut Context, args: &[Value]) -> Result<Value, String> {
285 if args.len() != 1 {
286 return Err("length requires exactly 1 argument".to_string());
287 }
288
289 let array_handle = match &args[0] {
290 Value::Array(handle) => handle,
291 _ => return Err("Argument must be an array".to_string()),
292 };
293
294 if let Some(heap) = _context.get_heap() {
296 if let Some(HeapEntry::Array(arr)) = heap.get(array_handle.id()) {
297 Ok(Value::Number(arr.len() as f64))
298 } else {
299 Err("Array not found in heap".to_string())
300 }
301 } else {
302 Err("Heap not available in context".to_string())
303 }
304 }
305}
306
307impl Default for Builtins {
308 fn default() -> Self {
309 Self::new()
310 }
311}