1#![allow(non_upper_case_globals)]
3
4use cl_ast::types::Symbol;
5
6use crate::{
7 Callable,
8 convalue::ConValue,
9 env::Environment,
10 error::{Error, ErrorKind, IResult},
11 place::Place,
12};
13use std::io::{Write, stdout};
14
15#[derive(Clone, Copy)]
17pub struct Builtin {
18 pub name: &'static str,
20 pub desc: &'static str,
22 pub func: &'static dyn Fn(&mut Environment, &[ConValue]) -> IResult<ConValue>,
24}
25
26impl Builtin {
27 pub const fn new(
29 name: &'static str,
30 desc: &'static str,
31 func: &'static impl Fn(&mut Environment, &[ConValue]) -> IResult<ConValue>,
32 ) -> Builtin {
33 Builtin { name, desc, func }
34 }
35
36 pub const fn description(&self) -> &'static str {
37 self.desc
38 }
39}
40
41impl std::fmt::Debug for Builtin {
42 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
43 f.debug_struct("Builtin")
44 .field("description", &self.desc)
45 .finish_non_exhaustive()
46 }
47}
48
49impl std::fmt::Display for Builtin {
50 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
51 f.write_str(self.desc)
52 }
53}
54
55impl super::Callable for Builtin {
56 fn call(&self, interpreter: &mut Environment, args: &[ConValue]) -> IResult<ConValue> {
57 (self.func)(interpreter, args)
58 }
59
60 fn name(&self) -> Option<Symbol> {
61 Some(self.name.into())
62 }
63}
64
65pub macro builtin(
83 $(#[$($meta:tt)*])*
84 fn $name:ident ($($arg:pat),*$(,)?) $(@$env:tt)? $body:block
85) {{
86 $(#[$($meta)*])*
87 fn $name(_env: &mut Environment, _args: &[ConValue]) -> IResult<ConValue> {
88 $(#[allow(unused)]let $env = _env;)?
90 #[allow(clippy::redundant_at_rest_pattern, irrefutable_let_patterns)]
92 let [$($arg),*] = _args else {
93 Err($crate::error::Error::TypeError(
94 concat!("(", $(stringify!($arg,),)* ")"),
95 $crate::typeinfo::Model::Any.intern()
96 ))?
97 };
98 $body.map(Into::into)
99 }
100 Builtin {
101 name: stringify!($name),
102 desc: stringify![builtin fn $name($($arg),*)],
103 func: &$name,
104 }
105}}
106
107pub macro builtins($(
109 $(#[$($meta:tt)*])*
110 fn $name:ident ($($args:tt)*) $(@$env:tt)? $body:block
111)*) {
112 [$(builtin!($(#[$($meta)*])* fn $name ($($args)*) $(@$env)? $body)),*]
113}
114
115pub macro error_format ($($t:tt)*) {
118 $crate::error::Error::BuiltinError(format!($($t)*))
119}
120
121pub const Builtins: &[Builtin] = &builtins![
122 fn fmt(args @ ..) @env {
124 use std::fmt::Write;
125 let mut out = String::new();
126
127 for mut arg in args.iter() {
128 while let ConValue::Ref(r) = arg {
129 arg = r.get(env)?;
130 }
131 if let Err(e) = write!(out, "{arg}") {
132 eprintln!("{e}");
133 }
134 }
135 Ok(out)
136 }
137
138 fn print(args @ ..) @env {
140 let mut out = stdout().lock();
141 for mut arg in args.iter() {
142 while let ConValue::Ref(r) = arg {
143 arg = r.get(env)?;
144 }
145 write!(out, "{arg}").ok();
146 }
147 Ok(())
148 }
149
150 fn println(args @ ..) @env {
152 let mut out = stdout().lock();
153 for mut arg in args.iter() {
154 while let ConValue::Ref(r) = arg {
155 arg = r.get(env)?;
156 }
157 write!(out, "{arg}").ok();
158 }
159 writeln!(out).ok();
160 Ok(())
161 }
162
163 fn dbg(arg) {
165 println!("{arg:?}");
166 Ok(arg.clone())
167 }
168
169 fn dbgp(args @ ..) {
171 let mut out = stdout().lock();
172 args.iter().try_for_each(|arg| writeln!(out, "{arg:#?}") ).ok();
173 Ok(())
174 }
175
176 fn bind(ConValue::Str(name), value) @env {
177 env.bind(*name, value.clone());
178 Ok(())
179 }
180
181 fn raw_ref(ConValue::Int(index)) {
183 Ok(ConValue::Ref(Place::from_index(*index as _)))
184 }
185
186 fn panic(args @ ..) @env {
187 use std::fmt::Write;
188 let mut stdout = stdout().lock();
189 let mut out = String::from("Explicit panic: ");
190 if let Err(e) = args.iter().try_for_each(|arg| write!(out, "{arg}")) {
191 writeln!(stdout, "{e}").ok();
192 }
193 writeln!(stdout, "{out}");
194 Err(Error::Panic(out))?;
195 Ok(())
196 }
197
198 fn todo(args @ ..) @env {
199 use std::fmt::Write;
200 let mut stdout = stdout().lock();
201 let mut out = String::from("Not yet implemented: ");
202 if let Err(e) = args.iter().try_for_each(|arg| write!(out, "{arg}")) {
203 writeln!(stdout, "{e}").ok();
204 }
205 writeln!(stdout, "{out}");
206 Err(Error::Panic(out))?;
207 Ok(())
208 }
209
210 fn dump() @env {
212 println!("{env}");
213 Ok(())
214 }
215
216 fn backtrace() @env {
217 println!("Backtrace:\n{}", env.backtrace());
218 Ok(())
219 }
220
221 fn host_backtrace() {
222 println!("Host backtrace:\n{}", std::backtrace::Backtrace::force_capture());
223 Ok(())
224 }
225
226 fn builtins() @env {
227 let len = env.globals().binds.len();
228 for builtin in 0..len {
229 if let Some(value @ ConValue::Builtin(_)) = env.get_id(builtin) {
230 println!("{builtin}: {value}")
231 }
232 }
233 Ok(())
234 }
235
236 fn len(list) @env {
238 Ok(match list.dereference_in(env)? {
239 ConValue::Empty => 0,
240 ConValue::Str(s) => s.chars().count() as _,
241 ConValue::String(s) => s.chars().count() as _,
242 &ConValue::Slice(_, start, end) => end as i128 - start as i128,
243 ConValue::Array(arr) => arr.len() as _,
244 ConValue::Tuple(t) => t.len() as _,
245 other => Err(Error::TypeError("A type with a length", other.type_of()))?,
246 })
247 }
248
249 fn push(ConValue::Ref(index), item) @env{
250 let mut index = index.get_mut(env)?;
251 while let ConValue::Ref(r) = index {
252 index = r.clone().get_mut(env)?;
253 }
254 let ConValue::Array(v) = index else {
255 Err(Error::TypeError("An array", index.type_of()))?
256 };
257
258 let mut items = std::mem::take(v).into_vec();
259 items.push(item.clone());
260 *v = items.into_boxed_slice();
261
262 Ok(ConValue::Empty)
263 }
264
265 fn pop(ConValue::Ref(index)) @env {
266 let v = match index.get_mut(env)? {
267 ConValue::Array(v) => v,
268 other => Err(Error::TypeError("An array", other.type_of()))?,
269 };
270
271 let mut items = std::mem::take(v).into_vec();
272 let out = items.pop().unwrap_or(ConValue::Empty);
273 *v = items.into_boxed_slice();
274
275 Ok(out)
276 }
277
278 fn chars(string) @env {
279 Ok(match string.dereference_in(env)? {
280 ConValue::Str(s) => ConValue::Array(s.chars().map(Into::into).collect()),
281 ConValue::String(s) => ConValue::Array(s.chars().map(Into::into).collect()),
282 _ => Err(Error::TypeError("string", string.type_of()))?,
283 })
284 }
285
286 fn invoke(function, args) @env {
288 match args {
289 ConValue::Empty => function.call(env, &[]),
290 ConValue::Array(args) | ConValue::Tuple(args) => function.call(env, args),
291 _ => function.call(env, std::slice::from_ref(args)),
292 }
293 }
294
295 fn dump_symbols() {
296 println!("{}", cl_structures::intern::string_interner::StringInterner::global());
297 Ok(ConValue::Empty)
298 }
299
300 fn catch_panic(lambda, args @ ..) @env {
301 match lambda.call(env, args) {
302 Err(Error { kind: ErrorKind::Panic(e, ..), ..}) => {
303 println!("Caught panic!");
304 Ok(ConValue::String(e))
305 },
306 other => other,
307 }
308 }
309
310 fn shark() {
312 Ok('\u{1f988}')
313 }
314];
315
316pub const Math: &[Builtin] = &builtins![
317 fn mul(lhs, rhs) {
319 Ok(match (lhs, rhs) {
320 (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
321 (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a * b),
322 _ => Err(Error::TypeError("type implements Mul", lhs.type_of()))?,
323 })
324 }
325
326 fn div(lhs, rhs) {
328 Ok(match (lhs, rhs){
329 (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
330 (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a / b),
331 _ => Err(Error::TypeError("type implements Div", lhs.type_of()))?,
332 })
333 }
334
335 fn rem(lhs, rhs) {
337 Ok(match (lhs, rhs) {
338 (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
339 (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a % b),
340 _ => Err(Error::TypeError("type implements Rem", lhs.type_of()))?,
341 })
342 }
343
344 fn add(lhs, rhs) {
346 Ok(match (lhs, rhs) {
347 (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
348 (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a + b),
349 (ConValue::Str(a), ConValue::Str(b)) => (a.to_string() + b).into(),
350 (ConValue::Str(a), ConValue::String(b)) => (a.to_string() + b).into(),
351 (ConValue::String(a), ConValue::Str(b)) => (a.to_string() + b).into(),
352 (ConValue::String(a), ConValue::String(b)) => (a.to_string() + b).into(),
353 (ConValue::Str(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(*c); s.into() }
354 (ConValue::String(s), ConValue::Char(c)) => { let mut s = s.to_string(); s.push(*c); s.into() }
355 (ConValue::Char(a), ConValue::Char(b)) => {
356 ConValue::String([a, b].into_iter().collect())
357 }
358 _ => Err(Error::TypeError("type implements Add", lhs.type_of()))?,
359 })
360 }
361
362 fn sub(lhs, rhs) {
364 Ok(match (lhs, rhs) {
365 (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
366 (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a - b),
367 _ => Err(Error::TypeError("type implements Sub", lhs.type_of()))?,
368 })
369 }
370
371 fn shl(lhs, rhs) {
373 Ok(match (lhs, rhs) {
374 (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
375 (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a << b),
376 (ConValue::Int(a), b) => Err(Error::TypeError("int", b.type_of()))?,
377 _ => Err(Error::TypeError("type implements Shl", lhs.type_of()))?,
378 })
379 }
380
381 fn shr(lhs, rhs) {
383 Ok(match (lhs, rhs) {
384 (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
385 (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a >> b),
386 (ConValue::Int(a), b) => Err(Error::TypeError("int", b.type_of()))?,
387 _ => Err(Error::TypeError("type implements Shr", lhs.type_of()))?,
388 })
389 }
390
391 fn and(lhs, rhs) {
393 Ok(match (lhs, rhs) {
394 (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
395 (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a & b),
396 (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a & b),
397 _ => Err(Error::TypeError("type implements BitAnd", lhs.type_of()))?,
398 })
399 }
400
401 fn or(lhs, rhs) {
403 Ok(match (lhs, rhs) {
404 (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
405 (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a | b),
406 (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a | b),
407 _ => Err(Error::TypeError("type implements BitOr", lhs.type_of()))?,
408 })
409 }
410
411 fn xor(lhs, rhs) {
413 Ok(match (lhs, rhs) {
414 (ConValue::Empty, ConValue::Empty) => ConValue::Empty,
415 (ConValue::Int(a), ConValue::Int(b)) => ConValue::Int(a ^ b),
416 (ConValue::Bool(a), ConValue::Bool(b)) => ConValue::Bool(a ^ b),
417 _ => Err(Error::TypeError("type implements BitXor", lhs.type_of()))?,
418 })
419 }
420
421 fn neg(tail) {
423 Ok(match tail {
424 ConValue::Empty => ConValue::Empty,
425 ConValue::Int(v) => ConValue::Int(-v),
426 ConValue::Float(v) => ConValue::Float(-v),
427 _ => Err(Error::TypeError("type implements Neg", tail.type_of()))?,
428 })
429 }
430
431 fn not(tail) {
433 Ok(match tail {
434 ConValue::Empty => ConValue::Empty,
435 ConValue::Int(v) => ConValue::Int(!v),
436 ConValue::Bool(v) => ConValue::Bool(!v),
437 _ => Err(Error::TypeError("type implements Not", tail.type_of()))?,
438 })
439 }
440
441 fn cmp(head, tail) {
443 Ok(ConValue::Int(match (head, tail) {
444 (ConValue::Int(a), ConValue::Int(b)) => a.cmp(b) as _,
445 (ConValue::Bool(a), ConValue::Bool(b)) => a.cmp(b) as _,
446 (ConValue::Char(a), ConValue::Char(b)) => a.cmp(b) as _,
447 (ConValue::Str(a), ConValue::Str(b)) => a.cmp(b) as _,
448 (ConValue::Str(a), ConValue::String(b)) => a.to_ref().cmp(b.as_str()) as _,
449 (ConValue::String(a), ConValue::Str(b)) => a.as_str().cmp(b.to_ref()) as _,
450 (ConValue::String(a), ConValue::String(b)) => a.cmp(b) as _,
451 _ => Err(error_format!("Incomparable values: {head}, {tail}"))?
452 }))
453 }
454
455 fn deref(tail) @env {
457 Ok(tail.dereference_in(env)?.clone())
458 }
459];