uni_core/
builtins.rs

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