jetcrab\runtime/
builtins.rs

1use 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        // Also register with simpler names for direct access
43        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    // Math functions
59    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    // String functions
177    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    // Array functions
230    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        // Get the array from the heap
241        if let Some(heap) = _context.get_heap() {
242            if let Some(HeapEntry::Array(arr)) = heap.get_mut(array_handle.id()) {
243                // Add all arguments except the first one (which is the array)
244                for arg in &args[1..] {
245                    arr.push(arg.clone());
246                }
247                
248                // Return the new length
249                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        // Get the array from the heap
269        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        // Get the array from the heap
295        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}