1use crate::compat::Rc;
2use crate::interpreter::Interpreter;
3use crate::value::Value;
4
5use crate::primitives::{
6 abs_builtin,
8 add_builtin,
10 bit_and_builtin,
12 bit_not_builtin,
13 bit_or_builtin,
14 bit_xor_builtin,
15 cons_builtin,
17 construct_record_builtin,
18 cr_builtin,
20 def_builtin,
22 div_builtin,
23 doc_builtin,
24 drop_builtin,
26 eq_builtin,
27 fetch_builtin, floor_div_builtin,
30 from_r_builtin,
31 get_record_field_builtin,
32 greater_equal_builtin,
33 greater_than_builtin,
34 head_builtin,
35 help_builtin,
36 is_record_type_builtin,
37 less_equal_builtin,
38 less_than_builtin,
40 list_builtin,
41 list_to_vector_builtin,
42 make_record_type_builtin,
43 make_vector_builtin,
44 max_builtin,
45 min_builtin,
46 mod_builtin,
47 mul_builtin,
48 not_equal_builtin,
49 null_predicate_builtin,
50 pick_builtin,
51 print_builtin,
54 words_builtin,
55 clear_builtin,
57 stack_builtin,
58 r_fetch_builtin,
59 record_type_of_builtin,
60 roll_builtin,
61 set_record_field_builtin,
62 shl_builtin,
64 shr_builtin,
65 space_builtin,
66 store_builtin, sub_builtin,
68 tail_builtin,
69 to_r_builtin,
71 to_string_builtin,
73 trunc_div_builtin,
74 truthy_predicate_builtin,
76 type_of_builtin,
78 val_builtin,
79 var_builtin,
80 vector_builtin,
81 vector_length_builtin,
82 vector_ref_builtin,
83 vector_set_builtin,
84 vector_to_list_builtin,
85};
86
87#[cfg(feature = "advanced_math")]
89use crate::primitives::{
90 ceil_builtin,
91 cos_builtin,
92 exp_builtin,
93 floor_builtin,
94 log_builtin,
95 pow_builtin,
96 round_builtin,
97 sin_builtin,
98 sqrt_builtin,
99 tan_builtin,
100};
101
102#[cfg(feature = "datetime")]
104use crate::primitives::{
105 date_add_builtin, date_equal_builtin, date_greater_than_builtin, date_less_than_builtin,
106 date_sub_builtin, datetime_builtin, datetime_to_string_builtin,
107 datetime_with_offset_builtin, day_builtin, duration_builtin, duration_equal_builtin,
108 duration_greater_than_builtin, duration_less_than_builtin, duration_to_seconds_builtin,
109 hour_builtin, minute_builtin, month_builtin, now_builtin, second_builtin,
110 string_to_datetime_builtin, timestamp_builtin, timestamp_to_datetime_builtin,
111 to_local_builtin, to_utc_builtin, weekday_builtin, year_builtin,
112};
113
114use crate::primitives::{
116 i16_avg_builtin, i16_buffer_builtin, i16_length_builtin, i16_max_builtin, i16_min_builtin,
117 i16_pop_builtin, i16_push_builtin, i16_ref_builtin, i16_set_builtin,
118};
119
120pub fn register_builtins(interp: &mut Interpreter) {
122 use crate::interpreter::DictEntry;
123
124 let add_atom = interp.intern_atom("+");
126 interp.dictionary.insert(
127 add_atom,
128 DictEntry {
129 value: Value::Builtin(add_builtin),
130 is_executable: true,
131 doc: Some(Rc::<str>::from(
132 "Add two numbers or concatenate strings.\nUsage: a b + => result\nExamples: 5 3 + => 8\n\"Hello \" \"World\" + => \"Hello World\"",
133 )),
134 },
135 );
136
137 let sub_atom = interp.intern_atom("-");
138 interp.dictionary.insert(
139 sub_atom,
140 DictEntry {
141 value: Value::Builtin(sub_builtin),
142 is_executable: true,
143 doc: Some(Rc::<str>::from(
144 "Subtract two numbers.\nUsage: a b - => result\nExample: 10 3 - => 7",
145 )),
146 },
147 );
148
149 let mul_atom = interp.intern_atom("*");
150 interp.dictionary.insert(
151 mul_atom,
152 DictEntry {
153 value: Value::Builtin(mul_builtin),
154 is_executable: true,
155 doc: Some(Rc::<str>::from(
156 "Multiply two numbers.\nUsage: a b * => result\nExample: 6 7 * => 42",
157 )),
158 },
159 );
160
161 let div_atom = interp.intern_atom("/");
162 interp.dictionary.insert(
163 div_atom,
164 DictEntry {
165 value: Value::Builtin(div_builtin),
166 is_executable: true,
167 doc: Some(Rc::<str>::from(
168 "Divide two numbers.\nUsage: a b / => result\nExample: 15 3 / => 5",
169 )),
170 },
171 );
172
173 let floor_div_atom = interp.intern_atom("//");
174 interp.dictionary.insert(
175 floor_div_atom,
176 DictEntry {
177 value: Value::Builtin(floor_div_builtin),
178 is_executable: true,
179 doc: Some(Rc::<str>::from(
180 "Floor division (like Python's //).\nUsage: a b // => result\nExample: 7 2 // => 3, -7 2 // => -4",
181 )),
182 },
183 );
184
185 let mod_atom = interp.intern_atom("mod");
186 interp.dictionary.insert(
187 mod_atom,
188 DictEntry {
189 value: Value::Builtin(mod_builtin),
190 is_executable: true,
191 doc: Some(Rc::<str>::from(
192 "Calculate modulo (remainder after division).\nUsage: a b mod => remainder\nExample: 10 3 mod => 1",
193 )),
194 },
195 );
196
197 let trunc_div_atom = interp.intern_atom("div");
198 interp.dictionary.insert(
199 trunc_div_atom,
200 DictEntry {
201 value: Value::Builtin(trunc_div_builtin),
202 is_executable: true,
203 doc: Some(Rc::<str>::from(
204 "Truncating integer division (rounds toward zero).\nUsage: a b div => result\nExample: 7 2 div => 3, -7 2 div => -3",
205 )),
206 },
207 );
208
209 let eq_atom = interp.intern_atom("=");
210 interp.dictionary.insert(
211 eq_atom,
212 DictEntry {
213 value: Value::Builtin(eq_builtin),
214 is_executable: true,
215 doc: Some(Rc::<str>::from(
216 "Test equality of two values.\nUsage: a b = => boolean\nExample: 5 5 = => true",
217 )),
218 },
219 );
220
221 let roll_atom = interp.intern_atom("roll");
223 interp.dictionary.insert(
224 roll_atom,
225 DictEntry {
226 value: Value::Builtin(roll_builtin),
227 is_executable: true,
228 doc: Some(Rc::<str>::from(
229 "Rotate n-th stack item to top.\nUsage: x1 x2 ... xn n roll => x2 ... xn x1\nExample: 1 2 3 2 roll => 2 3 1",
230 )),
231 },
232 );
233
234 let pick_atom = interp.intern_atom("pick");
235 interp.dictionary.insert(
236 pick_atom,
237 DictEntry {
238 value: Value::Builtin(pick_builtin),
239 is_executable: true,
240 doc: Some(Rc::<str>::from(
241 "Copy n-th stack item to top.\nUsage: x1 x2 ... xn n pick => x1 x2 ... xn x1\nExample: 1 2 3 2 pick => 1 2 3 1",
242 )),
243 },
244 );
245
246 let drop_atom = interp.intern_atom("drop");
247 interp.dictionary.insert(
248 drop_atom,
249 DictEntry {
250 value: Value::Builtin(drop_builtin),
251 is_executable: true,
252 doc: Some(Rc::<str>::from(
253 "Remove top stack item.\nUsage: x drop =>\nExample: 1 2 3 drop => 1 2",
254 )),
255 },
256 );
257
258 let def_atom = interp.intern_atom("def");
262 interp.dictionary.insert(
263 def_atom,
264 DictEntry {
265 value: Value::Builtin(def_builtin),
266 is_executable: true,
267 doc: Some(Rc::<str>::from(
268 "Define an executable word. Usage: 'name body def",
269 )),
270 },
271 );
272
273 let val_atom = interp.intern_atom("val");
274 interp.dictionary.insert(
275 val_atom,
276 DictEntry {
277 value: Value::Builtin(val_builtin),
278 is_executable: true,
279 doc: Some(Rc::<str>::from(
280 "Define a constant value. Usage: 'name value val",
281 )),
282 },
283 );
284
285 let var_atom = interp.intern_atom("var");
287 interp.dictionary.insert(
288 var_atom,
289 DictEntry {
290 value: Value::Builtin(var_builtin),
291 is_executable: true,
292 doc: Some(Rc::<str>::from(
293 "Create a mutable variable.\nUsage: initial-value 'name var\nExample: 0 'counter var",
294 )),
295 },
296 );
297
298 let fetch_atom = interp.intern_atom("@");
299 interp.dictionary.insert(
300 fetch_atom,
301 DictEntry {
302 value: Value::Builtin(fetch_builtin),
303 is_executable: true,
304 doc: Some(Rc::<str>::from(
305 "Fetch value from variable.\nUsage: var @ => value\nExample: counter @ .",
306 )),
307 },
308 );
309
310 let store_atom = interp.intern_atom("!");
311 interp.dictionary.insert(
312 store_atom,
313 DictEntry {
314 value: Value::Builtin(store_builtin),
315 is_executable: true,
316 doc: Some(Rc::<str>::from(
317 "Store value into variable.\nUsage: value var !\nExample: 42 counter !",
318 )),
319 },
320 );
321
322 let doc_atom = interp.intern_atom("doc");
323 interp.dictionary.insert(
324 doc_atom,
325 DictEntry {
326 value: Value::Builtin(doc_builtin),
327 is_executable: true,
328 doc: Some(Rc::<str>::from(
329 "Attach documentation to the most recent def/val.\nUsage: \"lines\" doc",
330 )),
331 },
332 );
333
334 let help_atom = interp.intern_atom("help");
335 interp.dictionary.insert(
336 help_atom,
337 DictEntry {
338 value: Value::Builtin(help_builtin),
339 is_executable: true,
340 doc: Some(Rc::<str>::from(
341 "Display documentation for a word. Usage: 'name help",
342 )),
343 },
344 );
345
346 let print_atom = interp.intern_atom(".");
348 interp.dictionary.insert(
349 print_atom,
350 DictEntry {
351 value: Value::Builtin(print_builtin),
352 is_executable: true,
353 doc: Some(Rc::<str>::from(
354 "Print top stack value to output.\nUsage: value . => (prints value)\nExample: \"Hello\" . => Hello",
355 )),
356 },
357 );
358
359 let words_atom = interp.intern_atom("words");
360 interp.dictionary.insert(
361 words_atom,
362 DictEntry {
363 value: Value::Builtin(words_builtin),
364 is_executable: true,
365 doc: Some(Rc::<str>::from(
366 "Display all defined words in the dictionary.\nUsage: words => (displays all words)\nExample: words => Defined words (120): ...",
367 )),
368 },
369 );
370
371 let cr_atom = interp.intern_atom("cr");
372 interp.dictionary.insert(
373 cr_atom,
374 DictEntry {
375 value: Value::Builtin(cr_builtin),
376 is_executable: true,
377 doc: Some(Rc::<str>::from(
378 "Output a newline character.\nUsage: cr => (outputs newline)\nExample: \"Hello\" . cr \"World\" . cr",
379 )),
380 },
381 );
382
383 let space_atom = interp.intern_atom("space");
384 interp.dictionary.insert(
385 space_atom,
386 DictEntry {
387 value: Value::Builtin(space_builtin),
388 is_executable: true,
389 doc: Some(Rc::<str>::from(
390 "Output a space character.\nUsage: space => (outputs space)\nExample: \"Hello\" . space \"World\" .",
391 )),
392 },
393 );
394
395 let stack_atom = interp.intern_atom("stack");
397 interp.dictionary.insert(
398 stack_atom,
399 DictEntry {
400 value: Value::Builtin(stack_builtin),
401 is_executable: true,
402 doc: Some(Rc::<str>::from(
403 "Display the current stack contents.\nUsage: stack => (displays stack)\nExample: 1 2 3 stack => Stack (3 items): ...",
404 )),
405 },
406 );
407
408 let clear_atom = interp.intern_atom("clear");
409 interp.dictionary.insert(
410 clear_atom,
411 DictEntry {
412 value: Value::Builtin(clear_builtin),
413 is_executable: true,
414 doc: Some(Rc::<str>::from(
415 "Clear all items from the stack.\nUsage: clear => (empties stack)\nExample: 1 2 3 clear stack => Stack is empty",
416 )),
417 },
418 );
419
420 let i16_buffer_atom = interp.intern_atom("i16-buffer");
422 interp.dictionary.insert(
423 i16_buffer_atom,
424 DictEntry {
425 value: Value::Builtin(i16_buffer_builtin),
426 is_executable: true,
427 doc: Some(Rc::<str>::from(
428 "Create i16 buffer of specified size.\nUsage: size i16-buffer => buffer\nExample: 1024 i16-buffer => #<i16-buffer:1024:[0 0 0 0 0 0 0 0 ...]>",
429 )),
430 },
431 );
432
433 let i16_ref_atom = interp.intern_atom("i16-ref");
434 interp.dictionary.insert(
435 i16_ref_atom,
436 DictEntry {
437 value: Value::Builtin(i16_ref_builtin),
438 is_executable: true,
439 doc: Some(Rc::<str>::from(
440 "Get value at index from i16 buffer.\nUsage: buffer index i16-ref => value\nExample: buffer 0 i16-ref => 100",
441 )),
442 },
443 );
444
445 let i16_set_atom = interp.intern_atom("i16-set!");
446 interp.dictionary.insert(
447 i16_set_atom,
448 DictEntry {
449 value: Value::Builtin(i16_set_builtin),
450 is_executable: true,
451 doc: Some(Rc::<str>::from(
452 "Set value at index in i16 buffer.\nUsage: value buffer index i16-set! => buffer\nExample: 100 buffer 0 i16-set! => buffer",
453 )),
454 },
455 );
456
457 let i16_length_atom = interp.intern_atom("i16-length");
458 interp.dictionary.insert(
459 i16_length_atom,
460 DictEntry {
461 value: Value::Builtin(i16_length_builtin),
462 is_executable: true,
463 doc: Some(Rc::<str>::from(
464 "Get length of i16 buffer.\nUsage: buffer i16-length => buffer length\nExample: buffer i16-length => buffer 1024",
465 )),
466 },
467 );
468
469 let i16_push_atom = interp.intern_atom("i16-push!");
470 interp.dictionary.insert(
471 i16_push_atom,
472 DictEntry {
473 value: Value::Builtin(i16_push_builtin),
474 is_executable: true,
475 doc: Some(Rc::<str>::from(
476 "Append value to end of i16 buffer.\nUsage: value buffer i16-push! => buffer\nExample: 100 buffer i16-push! => buffer",
477 )),
478 },
479 );
480
481 let i16_pop_atom = interp.intern_atom("i16-pop!");
482 interp.dictionary.insert(
483 i16_pop_atom,
484 DictEntry {
485 value: Value::Builtin(i16_pop_builtin),
486 is_executable: true,
487 doc: Some(Rc::<str>::from(
488 "Remove and return last value from i16 buffer.\nUsage: buffer i16-pop! => buffer value\nExample: buffer i16-pop! => buffer 100",
489 )),
490 },
491 );
492
493 let i16_max_atom = interp.intern_atom("i16-max");
494 interp.dictionary.insert(
495 i16_max_atom,
496 DictEntry {
497 value: Value::Builtin(i16_max_builtin),
498 is_executable: true,
499 doc: Some(Rc::<str>::from(
500 "Find maximum value in i16 buffer.\nUsage: buffer i16-max => buffer max-value\nExample: buffer i16-max => buffer 1000",
501 )),
502 },
503 );
504
505 let i16_min_atom = interp.intern_atom("i16-min");
506 interp.dictionary.insert(
507 i16_min_atom,
508 DictEntry {
509 value: Value::Builtin(i16_min_builtin),
510 is_executable: true,
511 doc: Some(Rc::<str>::from(
512 "Find minimum value in i16 buffer.\nUsage: buffer i16-min => buffer min-value\nExample: buffer i16-min => buffer -500",
513 )),
514 },
515 );
516
517 let i16_avg_atom = interp.intern_atom("i16-avg");
518 interp.dictionary.insert(
519 i16_avg_atom,
520 DictEntry {
521 value: Value::Builtin(i16_avg_builtin),
522 is_executable: true,
523 doc: Some(Rc::<str>::from(
524 "Compute average value of i16 buffer.\nUsage: buffer i16-avg => buffer average\nExample: buffer i16-avg => buffer 250",
525 )),
526 },
527 );
528
529 let to_string_atom = interp.intern_atom("->string");
531 interp.dictionary.insert(
532 to_string_atom,
533 DictEntry {
534 value: Value::Builtin(to_string_builtin),
535 is_executable: true,
536 doc: Some(Rc::<str>::from(
537 "Convert value to string representation.\nUsage: value ->string => string\nExample: 42 ->string => \"42\"",
538 )),
539 },
540 );
541
542 let head_atom = interp.intern_atom("head");
544 interp.dictionary.insert(
545 head_atom,
546 DictEntry {
547 value: Value::Builtin(head_builtin),
548 is_executable: true,
549 doc: Some(Rc::<str>::from(
550 "Get first element of a list.\nUsage: [x y z] head => x\nExample: [1 2 3] head => 1",
551 )),
552 },
553 );
554
555 let tail_atom = interp.intern_atom("tail");
556 interp.dictionary.insert(
557 tail_atom,
558 DictEntry {
559 value: Value::Builtin(tail_builtin),
560 is_executable: true,
561 doc: Some(Rc::<str>::from(
562 "Get all but first element of a list.\nUsage: [x y z] tail => [y z]\nExample: [1 2 3] tail => [2 3]",
563 )),
564 },
565 );
566
567 let cons_atom = interp.intern_atom("cons");
568 interp.dictionary.insert(
569 cons_atom,
570 DictEntry {
571 value: Value::Builtin(cons_builtin),
572 is_executable: true,
573 doc: Some(Rc::<str>::from(
574 "Prepend element to list (construct cons cell).\nUsage: x [y z] cons => [x y z]\nExample: 1 [2 3] cons => [1 2 3]",
575 )),
576 },
577 );
578
579 let list_atom = interp.intern_atom("list");
580 interp.dictionary.insert(
581 list_atom,
582 DictEntry {
583 value: Value::Builtin(list_builtin),
584 is_executable: true,
585 doc: Some(Rc::<str>::from(
586 "Convert all stack items into a list.\nUsage: x y z n list => [x y z]\nExample: 1 2 3 3 list => [1 2 3]",
587 )),
588 },
589 );
590
591 let vector_atom = interp.intern_atom("vector");
592 interp.dictionary.insert(
593 vector_atom,
594 DictEntry {
595 value: Value::Builtin(vector_builtin),
596 is_executable: true,
597 doc: Some(Rc::<str>::from(
598 "Create vector from stack items.\nUsage: x y z n vector => #[x y z]\nExample: 1 2 3 3 vector => #[1 2 3]",
599 )),
600 },
601 );
602
603 let make_vector_atom = interp.intern_atom("make-vector");
604 interp.dictionary.insert(
605 make_vector_atom,
606 DictEntry {
607 value: Value::Builtin(make_vector_builtin),
608 is_executable: true,
609 doc: Some(Rc::<str>::from(
610 "Create vector of size n filled with value.\nUsage: n value make-vector => #[value ...]\nExample: 3 0 make-vector => #[0 0 0]",
611 )),
612 },
613 );
614
615 let vector_length_atom = interp.intern_atom("vector-length");
616 interp.dictionary.insert(
617 vector_length_atom,
618 DictEntry {
619 value: Value::Builtin(vector_length_builtin),
620 is_executable: true,
621 doc: Some(Rc::<str>::from(
622 "Get length of a vector.\nUsage: #[a b c] vector-length => 3\nExample: #[1 2 3] vector-length => 3",
623 )),
624 },
625 );
626
627 let vector_ref_atom = interp.intern_atom("vector-ref");
628 interp.dictionary.insert(
629 vector_ref_atom,
630 DictEntry {
631 value: Value::Builtin(vector_ref_builtin),
632 is_executable: true,
633 doc: Some(Rc::<str>::from(
634 "Get element at index from vector.\nUsage: #[a b c] i vector-ref => element\nExample: #[10 20 30] 1 vector-ref => 20",
635 )),
636 },
637 );
638
639 let vector_set_atom = interp.intern_atom("vector-set!");
640 interp.dictionary.insert(
641 vector_set_atom,
642 DictEntry {
643 value: Value::Builtin(vector_set_builtin),
644 is_executable: true,
645 doc: Some(Rc::<str>::from(
646 "Set element at index in vector.\nUsage: value #[a b c] i vector-set! => #[a value c]\nExample: 99 #[10 20 30] 1 vector-set! => #[10 99 30]",
647 )),
648 },
649 );
650
651 let vector_to_list_atom = interp.intern_atom("vector->list");
652 interp.dictionary.insert(
653 vector_to_list_atom,
654 DictEntry {
655 value: Value::Builtin(vector_to_list_builtin),
656 is_executable: true,
657 doc: Some(Rc::<str>::from(
658 "Convert vector to list.\nUsage: #[a b c] vector->list => [a b c]\nExample: #[1 2 3] vector->list => [1 2 3]",
659 )),
660 },
661 );
662
663 let list_to_vector_atom = interp.intern_atom("list->vector");
664 interp.dictionary.insert(
665 list_to_vector_atom,
666 DictEntry {
667 value: Value::Builtin(list_to_vector_builtin),
668 is_executable: true,
669 doc: Some(Rc::<str>::from(
670 "Convert list to vector.\nUsage: [a b c] list->vector => #[a b c]\nExample: [1 2 3] list->vector => #[1 2 3]",
671 )),
672 },
673 );
674
675 let null_predicate_atom = interp.intern_atom("null?");
677 interp.dictionary.insert(
678 null_predicate_atom,
679 DictEntry {
680 value: Value::Builtin(null_predicate_builtin),
681 is_executable: true,
682 doc: Some(Rc::<str>::from(
683 "Test if value is null.\nUsage: value null? => boolean\nExample: null null? => true",
684 )),
685 },
686 );
687
688 let truthy_predicate_atom = interp.intern_atom("truthy?");
689 interp.dictionary.insert(
690 truthy_predicate_atom,
691 DictEntry {
692 value: Value::Builtin(truthy_predicate_builtin),
693 is_executable: true,
694 doc: Some(Rc::<str>::from(
695 "Test if value is truthy (non-null, non-false, non-zero).\nUsage: value truthy? => boolean\nExample: 5 truthy? => true",
696 )),
697 },
698 );
699
700 let type_of_atom = interp.intern_atom("type");
701 interp.dictionary.insert(
702 type_of_atom,
703 DictEntry {
704 value: Value::Builtin(type_of_builtin),
705 is_executable: true,
706 doc: Some(Rc::<str>::from(
707 "( x -- string ) Return type name of value as a string.\nTypes: number, integer, rational, gaussian, complex, atom, string, boolean, null, list, vector, nil, builtin",
708 )),
709 },
710 );
711
712 let less_than_atom = interp.intern_atom("<");
714 interp.dictionary.insert(
715 less_than_atom,
716 DictEntry {
717 value: Value::Builtin(less_than_builtin),
718 is_executable: true,
719 doc: Some(Rc::<str>::from(
720 "Test if first number is less than second.\nUsage: a b < => boolean\nExample: 3 5 < => true",
721 )),
722 },
723 );
724
725 let greater_than_atom = interp.intern_atom(">");
726 interp.dictionary.insert(
727 greater_than_atom,
728 DictEntry {
729 value: Value::Builtin(greater_than_builtin),
730 is_executable: true,
731 doc: Some(Rc::<str>::from(
732 "Test if first number is greater than second.\nUsage: a b > => boolean\nExample: 5 3 > => true",
733 )),
734 },
735 );
736
737 let less_equal_atom = interp.intern_atom("<=");
738 interp.dictionary.insert(
739 less_equal_atom,
740 DictEntry {
741 value: Value::Builtin(less_equal_builtin),
742 is_executable: true,
743 doc: Some(Rc::<str>::from(
744 "Test if first number is less than or equal to second.\nUsage: a b <= => boolean\nExample: 3 3 <= => true",
745 )),
746 },
747 );
748
749 let greater_equal_atom = interp.intern_atom(">=");
750 interp.dictionary.insert(
751 greater_equal_atom,
752 DictEntry {
753 value: Value::Builtin(greater_equal_builtin),
754 is_executable: true,
755 doc: Some(Rc::<str>::from(
756 "Test if first number is greater than or equal to second.\nUsage: a b >= => boolean\nExample: 5 5 >= => true",
757 )),
758 },
759 );
760
761 let not_equal_atom = interp.intern_atom("!=");
762 interp.dictionary.insert(
763 not_equal_atom,
764 DictEntry {
765 value: Value::Builtin(not_equal_builtin),
766 is_executable: true,
767 doc: Some(Rc::<str>::from(
768 "Test if two values are not equal.\nUsage: a b != => boolean\nExample: 5 3 != => true",
769 )),
770 },
771 );
772
773 let abs_atom = interp.intern_atom("abs");
775 interp.dictionary.insert(
776 abs_atom,
777 DictEntry {
778 value: Value::Builtin(abs_builtin),
779 is_executable: true,
780 doc: Some(Rc::<str>::from(
781 "Calculate absolute value.\nUsage: n abs => |n|\nExample: -5 abs => 5",
782 )),
783 },
784 );
785
786 let min_atom = interp.intern_atom("min");
787 interp.dictionary.insert(
788 min_atom,
789 DictEntry {
790 value: Value::Builtin(min_builtin),
791 is_executable: true,
792 doc: Some(Rc::<str>::from(
793 "Return minimum of two numbers.\nUsage: a b min => min(a,b)\nExample: 3 7 min => 3",
794 )),
795 },
796 );
797
798 let max_atom = interp.intern_atom("max");
799 interp.dictionary.insert(
800 max_atom,
801 DictEntry {
802 value: Value::Builtin(max_builtin),
803 is_executable: true,
804 doc: Some(Rc::<str>::from(
805 "Return maximum of two numbers.\nUsage: a b max => max(a,b)\nExample: 3 7 max => 7",
806 )),
807 },
808 );
809
810 #[cfg(feature = "advanced_math")]
811 {
812 let sqrt_atom = interp.intern_atom("sqrt");
813 interp.dictionary.insert(
814 sqrt_atom,
815 DictEntry {
816 value: Value::Builtin(sqrt_builtin),
817 is_executable: true,
818 doc: Some(Rc::<str>::from(
819 "Calculate square root.\nUsage: n sqrt => √n\nExample: 16 sqrt => 4",
820 )),
821 },
822 );
823 }
824
825 #[cfg(feature = "advanced_math")]
827 {
828 let pow_atom = interp.intern_atom("pow");
829 interp.dictionary.insert(
830 pow_atom,
831 DictEntry {
832 value: Value::Builtin(pow_builtin),
833 is_executable: true,
834 doc: Some(Rc::<str>::from(
835 "Raise number to power.\nUsage: base exponent pow => base^exponent\nExample: 2 8 pow => 256",
836 )),
837 },
838 );
839 }
840
841 #[cfg(feature = "advanced_math")]
842 {
843 let floor_atom = interp.intern_atom("floor");
844 interp.dictionary.insert(
845 floor_atom,
846 DictEntry {
847 value: Value::Builtin(floor_builtin),
848 is_executable: true,
849 doc: Some(Rc::<str>::from(
850 "Round down to nearest integer.\nUsage: n floor => ⌊n⌋\nExample: 3.7 floor => 3",
851 )),
852 },
853 );
854 }
855
856 #[cfg(feature = "advanced_math")]
857 {
858 let ceil_atom = interp.intern_atom("ceil");
859 interp.dictionary.insert(
860 ceil_atom,
861 DictEntry {
862 value: Value::Builtin(ceil_builtin),
863 is_executable: true,
864 doc: Some(Rc::<str>::from(
865 "Round up to nearest integer.\nUsage: n ceil => ⌈n⌉\nExample: 3.2 ceil => 4",
866 )),
867 },
868 );
869 }
870
871 #[cfg(feature = "advanced_math")]
872 {
873 let round_atom = interp.intern_atom("round");
874 interp.dictionary.insert(
875 round_atom,
876 DictEntry {
877 value: Value::Builtin(round_builtin),
878 is_executable: true,
879 doc: Some(Rc::<str>::from(
880 "Round to nearest integer.\nUsage: n round => round(n)\nExample: 3.5 round => 4",
881 )),
882 },
883 );
884 }
885
886 #[cfg(feature = "advanced_math")]
888 {
889 let sin_atom = interp.intern_atom("sin");
890 interp.dictionary.insert(
891 sin_atom,
892 DictEntry {
893 value: Value::Builtin(sin_builtin),
894 is_executable: true,
895 doc: Some(Rc::<str>::from(
896 "Calculate sine (radians).\nUsage: radians sin => sin(radians)\nExample: 0 sin => 0",
897 )),
898 },
899 );
900 }
901
902 #[cfg(feature = "advanced_math")]
903 {
904 let cos_atom = interp.intern_atom("cos");
905 interp.dictionary.insert(
906 cos_atom,
907 DictEntry {
908 value: Value::Builtin(cos_builtin),
909 is_executable: true,
910 doc: Some(Rc::<str>::from(
911 "Calculate cosine (radians).\nUsage: radians cos => cos(radians)\nExample: 0 cos => 1",
912 )),
913 },
914 );
915 }
916
917 #[cfg(feature = "advanced_math")]
918 {
919 let tan_atom = interp.intern_atom("tan");
920 interp.dictionary.insert(
921 tan_atom,
922 DictEntry {
923 value: Value::Builtin(tan_builtin),
924 is_executable: true,
925 doc: Some(Rc::<str>::from(
926 "Calculate tangent (radians).\nUsage: radians tan => tan(radians)\nExample: 0 tan => 0",
927 )),
928 },
929 );
930 }
931
932 #[cfg(feature = "advanced_math")]
934 {
935 let log_atom = interp.intern_atom("log");
936 interp.dictionary.insert(
937 log_atom,
938 DictEntry {
939 value: Value::Builtin(log_builtin),
940 is_executable: true,
941 doc: Some(Rc::<str>::from(
942 "Calculate natural logarithm.\nUsage: n log => ln(n)\nExample: 2.718281828 log => 1",
943 )),
944 },
945 );
946 }
947
948 #[cfg(feature = "advanced_math")]
949 {
950 let exp_atom = interp.intern_atom("exp");
951 interp.dictionary.insert(
952 exp_atom,
953 DictEntry {
954 value: Value::Builtin(exp_builtin),
955 is_executable: true,
956 doc: Some(Rc::<str>::from(
957 "Calculate e raised to power.\nUsage: n exp => e^n\nExample: 1 exp => 2.718281828",
958 )),
959 },
960 );
961 }
962
963 let bit_and_atom = interp.intern_atom("bit-and");
965 interp.dictionary.insert(
966 bit_and_atom,
967 DictEntry {
968 value: Value::Builtin(bit_and_builtin),
969 is_executable: true,
970 doc: Some(Rc::<str>::from(
971 "Bitwise AND of two integers.\nUsage: a b bit-and => a&b\nExample: 12 10 bit-and => 8",
972 )),
973 },
974 );
975
976 let bit_or_atom = interp.intern_atom("bit-or");
977 interp.dictionary.insert(
978 bit_or_atom,
979 DictEntry {
980 value: Value::Builtin(bit_or_builtin),
981 is_executable: true,
982 doc: Some(Rc::<str>::from(
983 "Bitwise OR of two integers.\nUsage: a b bit-or => a|b\nExample: 12 10 bit-or => 14",
984 )),
985 },
986 );
987
988 let bit_xor_atom = interp.intern_atom("bit-xor");
989 interp.dictionary.insert(
990 bit_xor_atom,
991 DictEntry {
992 value: Value::Builtin(bit_xor_builtin),
993 is_executable: true,
994 doc: Some(Rc::<str>::from(
995 "Bitwise XOR of two integers.\nUsage: a b bit-xor => a^b\nExample: 12 10 bit-xor => 6",
996 )),
997 },
998 );
999
1000 let bit_not_atom = interp.intern_atom("bit-not");
1001 interp.dictionary.insert(
1002 bit_not_atom,
1003 DictEntry {
1004 value: Value::Builtin(bit_not_builtin),
1005 is_executable: true,
1006 doc: Some(Rc::<str>::from(
1007 "Bitwise NOT (complement) of integer.\nUsage: n bit-not => ~n\nExample: 5 bit-not => -6",
1008 )),
1009 },
1010 );
1011
1012 let shl_atom = interp.intern_atom("shl");
1014 interp.dictionary.insert(
1015 shl_atom,
1016 DictEntry {
1017 value: Value::Builtin(shl_builtin),
1018 is_executable: true,
1019 doc: Some(Rc::<str>::from(
1020 "Shift bits left.\nUsage: n count shl => n<<count\nExample: 5 2 shl => 20",
1021 )),
1022 },
1023 );
1024
1025 let shr_atom = interp.intern_atom("shr");
1026 interp.dictionary.insert(
1027 shr_atom,
1028 DictEntry {
1029 value: Value::Builtin(shr_builtin),
1030 is_executable: true,
1031 doc: Some(Rc::<str>::from(
1032 "Shift bits right.\nUsage: n count shr => n>>count\nExample: 20 2 shr => 5",
1033 )),
1034 },
1035 );
1036
1037 let to_r_atom = interp.intern_atom(">r");
1039 interp.dictionary.insert(
1040 to_r_atom,
1041 DictEntry {
1042 value: Value::Builtin(to_r_builtin),
1043 is_executable: true,
1044 doc: Some(Rc::<str>::from(
1045 "Move value from data stack to return stack.\nUsage: x >r => (moves x to return stack)\nExample: 5 >r => (5 now on return stack)",
1046 )),
1047 },
1048 );
1049
1050 let from_r_atom = interp.intern_atom("r>");
1051 interp.dictionary.insert(
1052 from_r_atom,
1053 DictEntry {
1054 value: Value::Builtin(from_r_builtin),
1055 is_executable: true,
1056 doc: Some(Rc::<str>::from(
1057 "Move value from return stack to data stack.\nUsage: r> => x\nExample: r> => (moves top of return stack to data stack)",
1058 )),
1059 },
1060 );
1061
1062 let r_fetch_atom = interp.intern_atom("r@");
1063 interp.dictionary.insert(
1064 r_fetch_atom,
1065 DictEntry {
1066 value: Value::Builtin(r_fetch_builtin),
1067 is_executable: true,
1068 doc: Some(Rc::<str>::from(
1069 "Copy top of return stack to data stack.\nUsage: r@ => x\nExample: r@ => (copies top of return stack without removing it)",
1070 )),
1071 },
1072 );
1073
1074 let make_record_type_atom = interp.intern_atom("make-record-type");
1076 interp.dictionary.insert(
1077 make_record_type_atom,
1078 DictEntry {
1079 value: Value::Builtin(make_record_type_builtin),
1080 is_executable: true,
1081 doc: Some(Rc::<str>::from(
1082 "Create a record type with named fields.\nUsage: [field-names...] \"type-name\" make-record-type => record-type\nExample: [\"name\" \"age\"] \"person\" make-record-type",
1083 )),
1084 },
1085 );
1086
1087 let construct_record_atom = interp.intern_atom("construct-record");
1088 interp.dictionary.insert(
1089 construct_record_atom,
1090 DictEntry {
1091 value: Value::Builtin(construct_record_builtin),
1092 is_executable: true,
1093 doc: Some(Rc::<str>::from(
1094 "Internal helper to construct record instances.\nUsage: field-values... n \"type-name\" construct-record => record",
1095 )),
1096 },
1097 );
1098
1099 let is_record_type_atom = interp.intern_atom("is-record-type?");
1100 interp.dictionary.insert(
1101 is_record_type_atom,
1102 DictEntry {
1103 value: Value::Builtin(is_record_type_builtin),
1104 is_executable: true,
1105 doc: Some(Rc::<str>::from(
1106 "Check if value is a record of specific type.\nUsage: value \"type-name\" is-record-type? => boolean",
1107 )),
1108 },
1109 );
1110
1111 let get_record_field_atom = interp.intern_atom("get-record-field");
1112 interp.dictionary.insert(
1113 get_record_field_atom,
1114 DictEntry {
1115 value: Value::Builtin(get_record_field_builtin),
1116 is_executable: true,
1117 doc: Some(Rc::<str>::from(
1118 "Get field value from record.\nUsage: record \"type-name\" field-index get-record-field => value",
1119 )),
1120 },
1121 );
1122
1123 let set_record_field_atom = interp.intern_atom("set-record-field!");
1124 interp.dictionary.insert(
1125 set_record_field_atom,
1126 DictEntry {
1127 value: Value::Builtin(set_record_field_builtin),
1128 is_executable: true,
1129 doc: Some(Rc::<str>::from(
1130 "Set field value in record.\nUsage: new-value record \"type-name\" field-index set-record-field! => record",
1131 )),
1132 },
1133 );
1134
1135 let record_type_of_atom = interp.intern_atom("record-type-of");
1136 interp.dictionary.insert(
1137 record_type_of_atom,
1138 DictEntry {
1139 value: Value::Builtin(record_type_of_builtin),
1140 is_executable: true,
1141 doc: Some(Rc::<str>::from(
1142 "Get type name from record instance.\nUsage: record record-type-of => \"type-name\"",
1143 )),
1144 },
1145 );
1146
1147 #[cfg(feature = "datetime")]
1149 {
1150 let now_atom = interp.intern_atom("now");
1151 interp.dictionary.insert(
1152 now_atom,
1153 DictEntry {
1154 value: Value::Builtin(now_builtin),
1155 is_executable: true,
1156 doc: Some(Rc::<str>::from(
1157 "Get current date/time in local timezone.\nUsage: now => datetime",
1158 )),
1159 },
1160 );
1161
1162 let datetime_atom = interp.intern_atom("datetime");
1163 interp.dictionary.insert(
1164 datetime_atom,
1165 DictEntry {
1166 value: Value::Builtin(datetime_builtin),
1167 is_executable: true,
1168 doc: Some(Rc::<str>::from(
1169 "Create datetime in local timezone.\nUsage: year month day hour minute second datetime => datetime\nExample: 2025 10 1 14 30 0 datetime",
1170 )),
1171 },
1172 );
1173
1174 let datetime_with_offset_atom = interp.intern_atom("datetime-with-offset");
1175 interp.dictionary.insert(
1176 datetime_with_offset_atom,
1177 DictEntry {
1178 value: Value::Builtin(datetime_with_offset_builtin),
1179 is_executable: true,
1180 doc: Some(Rc::<str>::from(
1181 "Create datetime with specific offset.\nUsage: year month day hour minute second offset-hours datetime-with-offset => datetime\nExample: 2025 10 1 14 30 0 -5 datetime-with-offset",
1182 )),
1183 },
1184 );
1185
1186 let year_atom = interp.intern_atom("year");
1187 interp.dictionary.insert(
1188 year_atom,
1189 DictEntry {
1190 value: Value::Builtin(year_builtin),
1191 is_executable: true,
1192 doc: Some(Rc::<str>::from(
1193 "Extract year from datetime.\nUsage: datetime year => year",
1194 )),
1195 },
1196 );
1197
1198 let month_atom = interp.intern_atom("month");
1199 interp.dictionary.insert(
1200 month_atom,
1201 DictEntry {
1202 value: Value::Builtin(month_builtin),
1203 is_executable: true,
1204 doc: Some(Rc::<str>::from(
1205 "Extract month (1-12) from datetime.\nUsage: datetime month => month",
1206 )),
1207 },
1208 );
1209
1210 let day_atom = interp.intern_atom("day");
1211 interp.dictionary.insert(
1212 day_atom,
1213 DictEntry {
1214 value: Value::Builtin(day_builtin),
1215 is_executable: true,
1216 doc: Some(Rc::<str>::from(
1217 "Extract day (1-31) from datetime.\nUsage: datetime day => day",
1218 )),
1219 },
1220 );
1221
1222 let hour_atom = interp.intern_atom("hour");
1223 interp.dictionary.insert(
1224 hour_atom,
1225 DictEntry {
1226 value: Value::Builtin(hour_builtin),
1227 is_executable: true,
1228 doc: Some(Rc::<str>::from(
1229 "Extract hour (0-23) from datetime.\nUsage: datetime hour => hour",
1230 )),
1231 },
1232 );
1233
1234 let minute_atom = interp.intern_atom("minute");
1235 interp.dictionary.insert(
1236 minute_atom,
1237 DictEntry {
1238 value: Value::Builtin(minute_builtin),
1239 is_executable: true,
1240 doc: Some(Rc::<str>::from(
1241 "Extract minute (0-59) from datetime.\nUsage: datetime minute => minute",
1242 )),
1243 },
1244 );
1245
1246 let second_atom = interp.intern_atom("second");
1247 interp.dictionary.insert(
1248 second_atom,
1249 DictEntry {
1250 value: Value::Builtin(second_builtin),
1251 is_executable: true,
1252 doc: Some(Rc::<str>::from(
1253 "Extract second (0-59) from datetime.\nUsage: datetime second => second",
1254 )),
1255 },
1256 );
1257
1258 let weekday_atom = interp.intern_atom("weekday");
1259 interp.dictionary.insert(
1260 weekday_atom,
1261 DictEntry {
1262 value: Value::Builtin(weekday_builtin),
1263 is_executable: true,
1264 doc: Some(Rc::<str>::from(
1265 "Get day of week (0=Monday, 6=Sunday) from datetime.\nUsage: datetime weekday => weekday",
1266 )),
1267 },
1268 );
1269
1270 let timestamp_atom = interp.intern_atom("timestamp");
1271 interp.dictionary.insert(
1272 timestamp_atom,
1273 DictEntry {
1274 value: Value::Builtin(timestamp_builtin),
1275 is_executable: true,
1276 doc: Some(Rc::<str>::from(
1277 "Convert datetime to Unix timestamp (seconds since epoch).\nUsage: datetime timestamp => timestamp",
1278 )),
1279 },
1280 );
1281
1282 let timestamp_to_datetime_atom = interp.intern_atom("timestamp->datetime");
1283 interp.dictionary.insert(
1284 timestamp_to_datetime_atom,
1285 DictEntry {
1286 value: Value::Builtin(timestamp_to_datetime_builtin),
1287 is_executable: true,
1288 doc: Some(Rc::<str>::from(
1289 "Convert Unix timestamp to datetime in local timezone.\nUsage: timestamp timestamp->datetime => datetime",
1290 )),
1291 },
1292 );
1293
1294 let to_utc_atom = interp.intern_atom("to-utc");
1295 interp.dictionary.insert(
1296 to_utc_atom,
1297 DictEntry {
1298 value: Value::Builtin(to_utc_builtin),
1299 is_executable: true,
1300 doc: Some(Rc::<str>::from(
1301 "Convert datetime to UTC.\nUsage: datetime to-utc => datetime",
1302 )),
1303 },
1304 );
1305
1306 let to_local_atom = interp.intern_atom("to-local");
1307 interp.dictionary.insert(
1308 to_local_atom,
1309 DictEntry {
1310 value: Value::Builtin(to_local_builtin),
1311 is_executable: true,
1312 doc: Some(Rc::<str>::from(
1313 "Convert datetime to local timezone.\nUsage: datetime to-local => datetime",
1314 )),
1315 },
1316 );
1317
1318 let datetime_to_string_atom = interp.intern_atom("datetime->string");
1319 interp.dictionary.insert(
1320 datetime_to_string_atom,
1321 DictEntry {
1322 value: Value::Builtin(datetime_to_string_builtin),
1323 is_executable: true,
1324 doc: Some(Rc::<str>::from(
1325 "Format datetime as ISO 8601 string.\nUsage: datetime datetime->string => string",
1326 )),
1327 },
1328 );
1329
1330 let string_to_datetime_atom = interp.intern_atom("string->datetime");
1331 interp.dictionary.insert(
1332 string_to_datetime_atom,
1333 DictEntry {
1334 value: Value::Builtin(string_to_datetime_builtin),
1335 is_executable: true,
1336 doc: Some(Rc::<str>::from(
1337 "Parse ISO 8601 string to datetime.\nUsage: string string->datetime => datetime",
1338 )),
1339 },
1340 );
1341
1342 let date_less_than_atom = interp.intern_atom("date<");
1343 interp.dictionary.insert(
1344 date_less_than_atom,
1345 DictEntry {
1346 value: Value::Builtin(date_less_than_builtin),
1347 is_executable: true,
1348 doc: Some(Rc::<str>::from(
1349 "Compare datetimes (less than).\nUsage: datetime1 datetime2 date< => boolean",
1350 )),
1351 },
1352 );
1353
1354 let date_greater_than_atom = interp.intern_atom("date>");
1355 interp.dictionary.insert(
1356 date_greater_than_atom,
1357 DictEntry {
1358 value: Value::Builtin(date_greater_than_builtin),
1359 is_executable: true,
1360 doc: Some(Rc::<str>::from(
1361 "Compare datetimes (greater than).\nUsage: datetime1 datetime2 date> => boolean",
1362 )),
1363 },
1364 );
1365
1366 let date_equal_atom = interp.intern_atom("date=");
1367 interp.dictionary.insert(
1368 date_equal_atom,
1369 DictEntry {
1370 value: Value::Builtin(date_equal_builtin),
1371 is_executable: true,
1372 doc: Some(Rc::<str>::from(
1373 "Compare datetimes (equal).\nUsage: datetime1 datetime2 date= => boolean",
1374 )),
1375 },
1376 );
1377
1378 let duration_atom = interp.intern_atom("duration");
1380 interp.dictionary.insert(
1381 duration_atom,
1382 DictEntry {
1383 value: Value::Builtin(duration_builtin),
1384 is_executable: true,
1385 doc: Some(Rc::<str>::from(
1386 "Create duration from components.\nUsage: days hours minutes seconds duration => duration\nExample: 1 2 30 0 duration",
1387 )),
1388 },
1389 );
1390
1391 let duration_to_seconds_atom = interp.intern_atom("duration->seconds");
1392 interp.dictionary.insert(
1393 duration_to_seconds_atom,
1394 DictEntry {
1395 value: Value::Builtin(duration_to_seconds_builtin),
1396 is_executable: true,
1397 doc: Some(Rc::<str>::from(
1398 "Convert duration to total seconds.\nUsage: duration duration->seconds => seconds",
1399 )),
1400 },
1401 );
1402
1403 let date_add_atom = interp.intern_atom("date+");
1404 interp.dictionary.insert(
1405 date_add_atom,
1406 DictEntry {
1407 value: Value::Builtin(date_add_builtin),
1408 is_executable: true,
1409 doc: Some(Rc::<str>::from(
1410 "Add duration to datetime.\nUsage: datetime duration date+ => datetime",
1411 )),
1412 },
1413 );
1414
1415 let date_sub_atom = interp.intern_atom("date-");
1416 interp.dictionary.insert(
1417 date_sub_atom,
1418 DictEntry {
1419 value: Value::Builtin(date_sub_builtin),
1420 is_executable: true,
1421 doc: Some(Rc::<str>::from(
1422 "Subtract datetime or duration.\nUsage: datetime1 datetime2 date- => duration (time between)\nUsage: datetime duration date- => datetime (subtract duration)",
1423 )),
1424 },
1425 );
1426
1427 let duration_less_than_atom = interp.intern_atom("duration<");
1428 interp.dictionary.insert(
1429 duration_less_than_atom,
1430 DictEntry {
1431 value: Value::Builtin(duration_less_than_builtin),
1432 is_executable: true,
1433 doc: Some(Rc::<str>::from(
1434 "Compare durations (less than).\nUsage: duration1 duration2 duration< => boolean",
1435 )),
1436 },
1437 );
1438
1439 let duration_greater_than_atom = interp.intern_atom("duration>");
1440 interp.dictionary.insert(
1441 duration_greater_than_atom,
1442 DictEntry {
1443 value: Value::Builtin(duration_greater_than_builtin),
1444 is_executable: true,
1445 doc: Some(Rc::<str>::from(
1446 "Compare durations (greater than).\nUsage: duration1 duration2 duration> => boolean",
1447 )),
1448 },
1449 );
1450
1451 let duration_equal_atom = interp.intern_atom("duration=");
1452 interp.dictionary.insert(
1453 duration_equal_atom,
1454 DictEntry {
1455 value: Value::Builtin(duration_equal_builtin),
1456 is_executable: true,
1457 doc: Some(Rc::<str>::from(
1458 "Compare durations (equal).\nUsage: duration1 duration2 duration= => boolean",
1459 )),
1460 },
1461 );
1462 } #[cfg(feature = "hardware-microbit")]
1466 crate::hardware::microbit::register_microbit_primitives(interp);
1467
1468 #[cfg(feature = "hardware-pico")]
1469 crate::hardware::pico::register_pico_primitives(interp);
1470}
1471
1472#[cfg(test)]
1473mod tests {
1474 use super::*;
1475 use crate::interpreter::DictEntry;
1476
1477 fn setup_interpreter() -> Interpreter {
1478 Interpreter::new()
1479 }
1480
1481 #[test]
1482 fn test_all_builtins_registered() {
1483 let mut interp = setup_interpreter();
1484
1485 let expected_builtins = [
1486 "+", "-", "*", "/", "//", "div", "mod", "=", "<", ">", "<=", ">=", "!=", "abs", "min", "max", "sqrt", "pow", "floor", "ceil", "round", "sin", "cos", "tan", "log", "exp", "bit-and", "bit-or", "bit-xor", "bit-not", "shl", "shr", "roll", "pick", "drop", ">r", "r>", "r@", "def", "val", ".", "->string", "head", "tail", "cons", "list", "truthy?",
1503 ];
1504
1505 for builtin_name in expected_builtins.iter() {
1506 let atom = interp.intern_atom(builtin_name);
1507 assert!(
1508 interp.dictionary.contains_key(&atom),
1509 "Expected builtin '{}' to be registered",
1510 builtin_name
1511 );
1512
1513 assert!(matches!(
1515 interp.dictionary.get(&atom),
1516 Some(DictEntry {
1517 value: Value::Builtin(_),
1518 ..
1519 })
1520 ));
1521 }
1522 }
1523}