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