1use crate::{Heap, Value};
2use crate::devices::DeviceTable;
3use crate::devices::{console, CONSOLE_ID};
4use crate::value::{alloc_string, read_string};
5use alloc::collections::BTreeMap;
6use alloc::rc::Rc;
7use alloc::string::{String, ToString};
8use alloc::vec::Vec;
9
10pub mod fmath {
11 pub fn sqrt(x: f64) -> f64 { libm::sqrt(x) }
12 pub fn sin(x: f64) -> f64 { libm::sin(x) }
13 pub fn cos(x: f64) -> f64 { libm::cos(x) }
14 pub fn floor(x: f64) -> f64 { libm::floor(x) }
15 pub fn ceil(x: f64) -> f64 { libm::ceil(x) }
16 pub fn abs(x: f64) -> f64 { libm::fabs(x) }
17 pub fn fmax(a: f64, b: f64) -> f64 { libm::fmax(a, b) }
18 pub fn fmin(a: f64, b: f64) -> f64 { libm::fmin(a, b) }
19}
20
21pub struct NativeCtx<'a> {
22 pub heap: &'a mut Heap,
23 pub devices: &'a mut DeviceTable,
24 pub halted: &'a mut bool,
25 pub exit_code: &'a mut Option<i64>,
26}
27
28pub type NativeFn = Rc<dyn for<'a> Fn(&mut NativeCtx<'a>, &[Value]) -> Result<(Value, bool), String>>;
30
31#[derive(Default, Clone)]
32pub struct NativeRegistry {
33 fns: BTreeMap<String, NativeFn>,
34}
35
36impl NativeRegistry {
37 pub fn new() -> Self { Self { fns: BTreeMap::new() } }
38
39 pub fn register<S: Into<String>>(&mut self, name: S, func: NativeFn) {
40 self.fns.insert(name.into(), func);
41 }
42
43 pub fn get(&self, name: &str) -> Option<&NativeFn> {
44 self.fns.get(name)
45 }
46}
47
48pub fn register_default_builtins(reg: &mut NativeRegistry) {
49 reg.register("__concat", concat_native());
50 reg.register("__to_str", to_str_native());
51 reg.register("__float_abs", float_abs_native());
52 reg.register("__float_max", float_max_native());
53 reg.register("__float_min", float_min_native());
54 reg.register("__int_to_f", int_to_f_native());
55 reg.register("__char_to_f", char_to_f_native());
56 reg.register("__bool_to_f", bool_to_f_native());
57 reg.register("__float_to_i", float_to_i_native());
58 reg.register("__char_to_i", char_to_i_native());
59 reg.register("__bool_to_i", bool_to_i_native());
60 reg.register("__int_to_c", int_to_c_native());
61 reg.register("__int_to_s", int_to_s_native());
62 reg.register("__float_to_s", float_to_s_native());
63 reg.register("__bool_to_s", bool_to_s_native());
64 reg.register("__char_to_s", char_to_s_native());
65 reg.register("__string_to_s", string_to_s_native());
66 reg.register("__unit_to_s", unit_to_s_native());
67
68 reg.register("print", print_native());
69 reg.register("println", println_native());
70
71 reg.register("__int_abs", abs_native());
72 reg.register("ceil", ceil_native());
73 reg.register("flr", flr_native());
74 reg.register("cos", cos_native());
75 reg.register("sin", sin_native());
76 reg.register("sqrt", sqrt_native());
77 reg.register("__int_max", max_native());
78 reg.register("__int_min", min_native());
79
80 reg.register("halt", halt_native());
81 reg.register("abort", abort_native());
82
83 reg.register("__frame_present", frame_present_native());
86}
87
88fn frame_present_native() -> NativeFn {
89 Rc::new(|_ctx, _args| Err("__frame_present must be intercepted as a frame yield".into()))
90}
91
92#[inline]
93fn plain(v: Value) -> (Value, bool) { (v, false) }
94#[inline]
95fn handle(v: Value) -> (Value, bool) { (v, true) }
96
97fn concat_native() -> NativeFn {
98 Rc::new(|ctx, args| {
99 if args[0].is_handle_none() {
100 return Err(format!("__concat: arg0 not a String: {:?}", args[0]));
101 }
102 if args[1].is_handle_none() {
103 return Err(format!("__concat: arg1 not a String: {:?}", args[1]));
104 }
105 let (a_slot, a_gen) = args[0].as_handle();
106 let (b_slot, b_gen) = args[1].as_handle();
107
108 let a_len = {
112 let d = ctx.heap.cell_data(a_slot, a_gen)?;
113 (d[0] as usize).min(d.len().saturating_sub(1) * 8)
114 };
115 let b_len = {
116 let d = ctx.heap.cell_data(b_slot, b_gen)?;
117 (d[0] as usize).min(d.len().saturating_sub(1) * 8)
118 };
119 let total = a_len + b_len;
120 let size = 1 + (total + 7) / 8;
121
122 let (slot, gen_) = ctx.heap.try_alloc(size)?;
123
124 let a_src = ctx.heap.cell_data(a_slot, a_gen)?[1..].as_ptr() as *const u8;
127 let b_src = ctx.heap.cell_data(b_slot, b_gen)?[1..].as_ptr() as *const u8;
128
129 let dst = ctx.heap.cell_data_mut(slot, gen_)?;
130 dst[0] = total as u64;
131 let dst_ptr = dst[1..].as_mut_ptr() as *mut u8;
132 unsafe {
133 core::ptr::copy_nonoverlapping(a_src, dst_ptr, a_len);
134 core::ptr::copy_nonoverlapping(b_src, dst_ptr.add(a_len), b_len);
135 }
136
137 Ok(handle(Value::from_handle(slot, gen_)))
138 })
139}
140
141fn to_str_native() -> NativeFn {
142 Rc::new(|ctx, args| {
144 if let Some(s) = read_string(ctx.heap, args[0]) {
145 let v = alloc_string(ctx.heap, &s)?;
146 return Ok(handle(v));
147 }
148 let v = alloc_string(ctx.heap, &args[0].as_int().to_string())?;
149 Ok(handle(v))
150 })
151}
152
153fn print_native() -> NativeFn {
154 Rc::new(|ctx, args| {
155 if args[0].is_handle_none() {
156 return Err(format!("print: arg0 not a String: {:?}", args[0]));
157 }
158 let (slot, gen_) = args[0].as_handle();
159 let bytes: Vec<u8> = {
160 let d = ctx.heap.cell_data(slot, gen_)?;
161 let len = (d[0] as usize).min(d.len().saturating_sub(1) * 8);
163 let ptr = d[1..].as_ptr() as *const u8;
164 unsafe { core::slice::from_raw_parts(ptr, len).to_vec() }
165 };
166 write_console(ctx.devices, ctx.heap, &bytes, "print")?;
167 Ok(plain(Value::ZERO))
168 })
169}
170
171fn println_native() -> NativeFn {
172 Rc::new(|ctx, args| {
173 if args[0].is_handle_none() {
174 return Err(format!("println: arg0 not a String: {:?}", args[0]));
175 }
176 let (slot, gen_) = args[0].as_handle();
177 let bytes: Vec<u8> = {
178 let d = ctx.heap.cell_data(slot, gen_)?;
179 let len = (d[0] as usize).min(d.len().saturating_sub(1) * 8);
180 let ptr = d[1..].as_ptr() as *const u8;
181 unsafe { core::slice::from_raw_parts(ptr, len).to_vec() }
182 };
183 write_console(ctx.devices, ctx.heap, &bytes, "println")?;
184 write_console(ctx.devices, ctx.heap, b"\n", "println")?;
185 Ok(plain(Value::ZERO))
186 })
187}
188
189fn write_console(devices: &mut DeviceTable, heap: &mut Heap, bytes: &[u8], op: &str) -> Result<(), String> {
190 let dev = devices.get_mut(CONSOLE_ID)
191 .ok_or_else(|| format!("{}: Console device 0x{:02x} not installed", op, CONSOLE_ID))?;
192 dev.write_bytes(console::PORT_STDOUT, bytes, heap)
193}
194
195fn halt_native() -> NativeFn {
196 Rc::new(|ctx, args| {
197 let code = args[0].as_int();
198 *ctx.exit_code = Some(code & 0xFFFF_FFFF);
199 *ctx.halted = true;
200 Ok(plain(Value::ZERO))
201 })
202}
203
204fn abort_native() -> NativeFn {
205 Rc::new(|ctx, args| {
206 let msg = read_string(ctx.heap, args[0])
207 .ok_or_else(|| format!("abort: arg0 not a String: {:?}", args[0]))?;
208 Err(format!("abort: {}", msg))
209 })
210}
211
212fn abs_native() -> NativeFn {
213 Rc::new(|_ctx, args| {
214 let n = args[0].as_int();
215 Ok(plain(Value::from_int(n.wrapping_abs())))
216 })
217}
218
219fn max_native() -> NativeFn {
220 Rc::new(|_ctx, args| {
221 let a = args[0].as_int();
222 let b = args[1].as_int();
223 Ok(plain(Value::from_int(a.max(b))))
224 })
225}
226
227fn min_native() -> NativeFn {
228 Rc::new(|_ctx, args| {
229 let a = args[0].as_int();
230 let b = args[1].as_int();
231 Ok(plain(Value::from_int(a.min(b))))
232 })
233}
234
235fn float_abs_native() -> NativeFn {
236 Rc::new(|_ctx, args| Ok(plain(Value::from_float(fmath::abs(args[0].as_float())))))
237}
238
239fn float_max_native() -> NativeFn {
240 Rc::new(|_ctx, args| Ok(plain(Value::from_float(fmath::fmax(args[0].as_float(), args[1].as_float())))))
241}
242
243fn float_min_native() -> NativeFn {
244 Rc::new(|_ctx, args| Ok(plain(Value::from_float(fmath::fmin(args[0].as_float(), args[1].as_float())))))
245}
246
247fn int_to_f_native() -> NativeFn {
248 Rc::new(|_ctx, args| Ok(plain(Value::from_float(args[0].as_int() as f64))))
249}
250
251fn char_to_f_native() -> NativeFn {
252 Rc::new(|_ctx, args| {
253 let c = args[0].as_char().ok_or_else(|| format!("__char_to_f: arg0 not Char: {:?}", args[0]))?;
254 Ok(plain(Value::from_float(c as u32 as f64)))
255 })
256}
257
258fn bool_to_f_native() -> NativeFn {
259 Rc::new(|_ctx, args| Ok(plain(Value::from_float(if args[0].as_bool() { 1.0 } else { 0.0 }))))
260}
261
262fn float_to_i_native() -> NativeFn {
263 Rc::new(|_ctx, args| Ok(plain(Value::from_int(args[0].as_float() as i64))))
264}
265
266fn char_to_i_native() -> NativeFn {
267 Rc::new(|_ctx, args| {
268 let c = args[0].as_char().ok_or_else(|| format!("__char_to_i: arg0 not Char: {:?}", args[0]))?;
269 Ok(plain(Value::from_int(c as i64)))
270 })
271}
272
273fn bool_to_i_native() -> NativeFn {
274 Rc::new(|_ctx, args| Ok(plain(Value::from_int(if args[0].as_bool() { 1 } else { 0 }))))
275}
276
277fn int_to_c_native() -> NativeFn {
278 Rc::new(|_ctx, args| {
279 let n = args[0].as_int();
280 let u = u32::try_from(n).map_err(|_| format!("abort: invalid codepoint {}", n))?;
281 let c = char::from_u32(u).ok_or_else(|| format!("abort: invalid codepoint U+{:X}", u))?;
282 Ok(plain(Value::from_char(c)))
283 })
284}
285
286fn int_to_s_native() -> NativeFn {
287 Rc::new(|ctx, args| {
288 let v = alloc_string(ctx.heap, &args[0].as_int().to_string())?;
289 Ok(handle(v))
290 })
291}
292
293fn float_to_s_native() -> NativeFn {
294 Rc::new(|ctx, args| {
295 let v = alloc_string(ctx.heap, &args[0].as_float().to_string())?;
296 Ok(handle(v))
297 })
298}
299
300fn bool_to_s_native() -> NativeFn {
301 Rc::new(|ctx, args| {
302 let v = alloc_string(ctx.heap, &args[0].as_bool().to_string())?;
303 Ok(handle(v))
304 })
305}
306
307fn char_to_s_native() -> NativeFn {
308 Rc::new(|ctx, args| {
309 let c = args[0].as_char().ok_or_else(|| format!("__char_to_s: arg0 not Char: {:?}", args[0]))?;
310 let v = alloc_string(ctx.heap, &c.to_string())?;
311 Ok(handle(v))
312 })
313}
314
315fn string_to_s_native() -> NativeFn {
316 Rc::new(|ctx, args| {
317 if args[0].is_handle_none() {
318 return Err(format!("__string_to_s: arg0 not String: {:?}", args[0]));
319 }
320 let (slot, gen_) = args[0].as_handle();
321 ctx.heap.rc_inc(slot, gen_)?;
322 Ok(handle(args[0]))
323 })
324}
325
326fn unit_to_s_native() -> NativeFn {
327 Rc::new(|ctx, _args| {
328 let v = alloc_string(ctx.heap, "()")?;
329 Ok(handle(v))
330 })
331}
332
333fn ceil_native() -> NativeFn {
334 Rc::new(|_ctx, args| Ok(plain(Value::from_float(fmath::ceil(args[0].as_float())))))
335}
336
337fn flr_native() -> NativeFn {
338 Rc::new(|_ctx, args| Ok(plain(Value::from_float(fmath::floor(args[0].as_float())))))
339}
340
341fn cos_native() -> NativeFn { Rc::new(|_ctx, args| Ok(plain(Value::from_float(fmath::cos(args[0].as_float()))))) }
342fn sin_native() -> NativeFn { Rc::new(|_ctx, args| Ok(plain(Value::from_float(fmath::sin(args[0].as_float()))))) }
343fn sqrt_native() -> NativeFn { Rc::new(|_ctx, args| Ok(plain(Value::from_float(fmath::sqrt(args[0].as_float()))))) }