datex_core/libs/
core.rs

1use crate::collections::HashMap;
2use crate::references::reference::Reference;
3use crate::references::type_reference::{
4    NominalTypeDeclaration, TypeReference,
5};
6use crate::runtime::memory::Memory;
7use crate::stdlib::boxed::Box;
8use crate::stdlib::format;
9use crate::stdlib::rc::Rc;
10use crate::stdlib::string::String;
11use crate::stdlib::string::ToString;
12use crate::stdlib::vec;
13use crate::stdlib::vec::Vec;
14use crate::types::definition::TypeDefinition;
15use crate::types::structural_type_definition::StructuralTypeDefinition;
16use crate::values::core_value::CoreValue;
17use crate::values::core_values::callable::{
18    Callable, CallableBody, CallableKind, CallableSignature,
19};
20use crate::values::core_values::decimal::typed_decimal::DecimalTypeVariant;
21use crate::values::core_values::integer::typed_integer::IntegerTypeVariant;
22use crate::values::core_values::map::Map;
23use crate::values::core_values::r#type::Type;
24use crate::values::pointer::PointerAddress;
25use crate::values::value::Value;
26use crate::values::value_container::ValueContainer;
27use core::cell::RefCell;
28use core::iter::once;
29use core::prelude::rust_2024::*;
30use core::result::Result;
31use datex_core::values::core_values::integer::Integer;
32use datex_macros::LibTypeString;
33use log::info;
34use strum::IntoEnumIterator;
35
36type CoreLibTypes = HashMap<CoreLibPointerId, Type>;
37type CoreLibVals = HashMap<CoreLibPointerId, ValueContainer>;
38
39#[cfg_attr(not(feature = "embassy_runtime"), thread_local)]
40pub static mut CORE_LIB_TYPES: Option<CoreLibTypes> = None;
41
42#[cfg_attr(not(feature = "embassy_runtime"), thread_local)]
43pub static mut CORE_LIB_VALS: Option<CoreLibVals> = None;
44
45fn with_full_core_lib<R>(
46    handler: impl FnOnce(&CoreLibTypes, &CoreLibVals) -> R,
47) -> R {
48    unsafe {
49        if CORE_LIB_TYPES.is_none() {
50            CORE_LIB_TYPES.replace(create_core_lib_types());
51        }
52        if CORE_LIB_VALS.is_none() {
53            CORE_LIB_VALS.replace(create_core_lib_vals());
54        }
55        handler(
56            CORE_LIB_TYPES.as_ref().unwrap_unchecked(),
57            CORE_LIB_VALS.as_ref().unwrap_unchecked(),
58        )
59    }
60}
61
62fn with_core_lib_types<R>(handler: impl FnOnce(&CoreLibTypes) -> R) -> R {
63    unsafe {
64        if CORE_LIB_TYPES.is_none() {
65            CORE_LIB_TYPES.replace(create_core_lib_types());
66        }
67        handler(CORE_LIB_TYPES.as_ref().unwrap_unchecked())
68    }
69}
70
71#[derive(Debug, Clone, PartialEq, Eq, Hash, LibTypeString)]
72pub enum CoreLibPointerId {
73    Core,                                // #core
74    Type,                                // #core.type
75    Null,                                // #core.null
76    Boolean,                             // #core.boolean
77    Integer(Option<IntegerTypeVariant>), // #core.integer
78    Decimal(Option<DecimalTypeVariant>), // #core.decimal
79    Text,                                // #core.text
80    Endpoint,                            // #core.endpoint
81    List,                                // #core.List
82    Map,                                 // #core.Map
83    Callable,                            // #core.Callable
84    Unit,                                // #core.Unit
85    Never,                               // #core.never
86    Unknown,                             // #core.unknown
87    Print, // #core.print (function, might be removed later)
88}
89
90impl CoreLibPointerId {
91    const INTEGER_BASE: u16 = 100;
92    const DECIMAL_BASE: u16 = 300;
93
94    pub fn to_u16(&self) -> u16 {
95        match self {
96            CoreLibPointerId::Core => 0,
97            CoreLibPointerId::Null => 1,
98            CoreLibPointerId::Type => 2,
99            CoreLibPointerId::Boolean => 3,
100            CoreLibPointerId::Callable => 5,
101            CoreLibPointerId::Endpoint => 7,
102            CoreLibPointerId::Text => 8,
103            CoreLibPointerId::List => 9,
104            CoreLibPointerId::Unit => 11,
105            CoreLibPointerId::Map => 12,
106            CoreLibPointerId::Never => 13,
107            CoreLibPointerId::Unknown => 14,
108            CoreLibPointerId::Print => 15,
109            CoreLibPointerId::Integer(None) => Self::INTEGER_BASE,
110            CoreLibPointerId::Integer(Some(v)) => {
111                let v: u8 = (*v).into();
112                CoreLibPointerId::Integer(None).to_u16() + v as u16
113            }
114            CoreLibPointerId::Decimal(None) => Self::DECIMAL_BASE,
115            CoreLibPointerId::Decimal(Some(v)) => {
116                let v: u8 = (*v).into();
117                CoreLibPointerId::Decimal(None).to_u16() + v as u16
118            }
119        }
120    }
121
122    pub fn from_u16(id: u16) -> Option<Self> {
123        match id {
124            0 => Some(CoreLibPointerId::Core),
125            1 => Some(CoreLibPointerId::Null),
126            2 => Some(CoreLibPointerId::Type),
127            3 => Some(CoreLibPointerId::Boolean),
128            5 => Some(CoreLibPointerId::Callable),
129            7 => Some(CoreLibPointerId::Endpoint),
130            8 => Some(CoreLibPointerId::Text),
131            9 => Some(CoreLibPointerId::List),
132            11 => Some(CoreLibPointerId::Unit),
133            12 => Some(CoreLibPointerId::Map),
134            13 => Some(CoreLibPointerId::Never),
135            14 => Some(CoreLibPointerId::Unknown),
136            15 => Some(CoreLibPointerId::Print),
137
138            Self::INTEGER_BASE => Some(CoreLibPointerId::Integer(None)),
139            n if (Self::INTEGER_BASE + 1..Self::DECIMAL_BASE).contains(&n) => {
140                IntegerTypeVariant::try_from((n - Self::INTEGER_BASE) as u8)
141                    .ok()
142                    .map(|v| CoreLibPointerId::Integer(Some(v)))
143            }
144
145            Self::DECIMAL_BASE => Some(CoreLibPointerId::Decimal(None)),
146            n if n > Self::DECIMAL_BASE => {
147                DecimalTypeVariant::try_from((n - Self::DECIMAL_BASE) as u8)
148                    .ok()
149                    .map(|v| CoreLibPointerId::Decimal(Some(v)))
150            }
151
152            _ => None,
153        }
154    }
155}
156
157impl From<CoreLibPointerId> for PointerAddress {
158    fn from(id: CoreLibPointerId) -> Self {
159        let id_bytes: [u8; 3] =
160            (id.to_u16() as u32).to_le_bytes()[0..3].try_into().unwrap();
161        PointerAddress::Internal(id_bytes)
162    }
163}
164
165impl TryFrom<&PointerAddress> for CoreLibPointerId {
166    type Error = String;
167    fn try_from(address: &PointerAddress) -> Result<Self, Self::Error> {
168        match address {
169            PointerAddress::Internal(id_bytes) => {
170                let mut id_array = [0u8; 4];
171                id_array[0..3].copy_from_slice(id_bytes);
172                let id = u32::from_le_bytes(id_array);
173                match CoreLibPointerId::from_u16(id as u16) {
174                    Some(core_id) => Ok(core_id),
175                    None => Err("Invalid CoreLibPointerId".to_string()),
176                }
177            }
178            e => Err(format!(
179                "CoreLibPointerId can only be created from Internal PointerAddress, got: {:?}",
180                e
181            )),
182        }
183    }
184}
185
186pub fn get_core_lib_type(id: impl Into<CoreLibPointerId>) -> Type {
187    with_core_lib_types(|core_lib_types| {
188        core_lib_types.get(&id.into()).unwrap().clone()
189    })
190}
191
192pub fn get_core_lib_type_reference(
193    id: impl Into<CoreLibPointerId>,
194) -> Rc<RefCell<TypeReference>> {
195    let type_container = get_core_lib_type(id);
196    match type_container.type_definition {
197        TypeDefinition::Reference(tr) => tr,
198        _ => core::panic!("Core lib type is not a TypeReference"),
199    }
200}
201
202/// Retrieves either a core library type or value by its CoreLibPointerId.
203pub fn get_core_lib_value(
204    id: impl Into<CoreLibPointerId>,
205) -> Option<ValueContainer> {
206    let id = id.into();
207    with_full_core_lib(|core_lib_types, core_lib_values| {
208        // try types first
209        if let Some(ty) = core_lib_types.get(&id) {
210            match &ty.type_definition {
211                TypeDefinition::Reference(tr) => {
212                    Some(ValueContainer::Reference(Reference::TypeReference(
213                        tr.clone(),
214                    )))
215                }
216                _ => core::panic!("Core lib type is not a TypeReference"),
217            }
218        } else if let Some(val) = core_lib_values.get(&id) {
219            Some(val.clone())
220        } else {
221            None
222        }
223    })
224}
225
226pub fn get_core_lib_type_definition(
227    id: impl Into<CoreLibPointerId>,
228) -> TypeDefinition {
229    get_core_lib_type(id).type_definition
230}
231
232fn has_core_lib_type<T>(id: T) -> bool
233where
234    T: Into<CoreLibPointerId>,
235{
236    with_core_lib_types(|core_lib_types| {
237        core_lib_types.contains_key(&id.into())
238    })
239}
240
241/// Loads the core library into the provided memory instance.
242pub fn load_core_lib(memory: &mut Memory) {
243    with_full_core_lib(|core_lib_types, core_lib_values| {
244        let mut types_structure = core_lib_types
245            .values()
246            .map(|ty| match &ty.type_definition {
247                TypeDefinition::Reference(type_reference) => {
248                    let name = type_reference
249                        .borrow()
250                        .nominal_type_declaration
251                        .as_ref()
252                        .unwrap()
253                        .to_string();
254                    let reference =
255                        Reference::TypeReference(type_reference.clone());
256                    memory.register_reference(&reference);
257                    (name, ValueContainer::Reference(reference))
258                }
259                _ => core::panic!("Core lib type is not a TypeReference"),
260            })
261            .collect::<Vec<(String, ValueContainer)>>();
262
263        // add core lib values
264        for (name, val) in core_lib_values.iter() {
265            let name = name.to_string();
266            types_structure.push((name, val.clone()));
267        }
268
269        // TODO #455: dont store variants as separate entries in core_struct (e.g., integer/u8, integer/i32, only keep integer)
270        // Import variants directly by variant access operator from base type (e.g., integer -> integer/u8)
271        let core_struct = Reference::from(ValueContainer::from(
272            Map::from_iter(types_structure),
273        ));
274        core_struct.set_pointer_address(CoreLibPointerId::Core.into());
275        memory.register_reference(&core_struct);
276    });
277}
278
279/// Creates a new instance of the core library as a ValueContainer
280/// including all core types as properties.
281pub fn create_core_lib_types() -> HashMap<CoreLibPointerId, Type> {
282    let integer = integer();
283    let decimal = decimal();
284    vec![
285        ty(),
286        text(),
287        list(),
288        boolean(),
289        endpoint(),
290        unit(),
291        never(),
292        unknown(),
293        map(),
294        null(),
295        callable(),
296    ]
297    .into_iter()
298    .chain(once(integer.clone()))
299    .chain(
300        IntegerTypeVariant::iter()
301            .map(|variant| integer_variant(integer.1.clone(), variant)),
302    )
303    .chain(once(decimal.clone()))
304    .chain(
305        DecimalTypeVariant::iter()
306            .map(|variant| decimal_variant(decimal.1.clone(), variant)),
307    )
308    .collect::<HashMap<CoreLibPointerId, Type>>()
309}
310
311pub fn create_core_lib_vals() -> HashMap<CoreLibPointerId, ValueContainer> {
312    vec![print()]
313        .into_iter()
314        .collect::<HashMap<CoreLibPointerId, ValueContainer>>()
315}
316
317type CoreLibTypeDefinition = (CoreLibPointerId, Type);
318pub fn ty() -> CoreLibTypeDefinition {
319    create_core_type("type", None, None, CoreLibPointerId::Type)
320}
321pub fn null() -> CoreLibTypeDefinition {
322    create_core_type("null", None, None, CoreLibPointerId::Null)
323}
324pub fn list() -> CoreLibTypeDefinition {
325    create_core_type("List", None, None, CoreLibPointerId::List)
326}
327pub fn map() -> CoreLibTypeDefinition {
328    create_core_type("Map", None, None, CoreLibPointerId::Map)
329}
330
331pub fn unit() -> CoreLibTypeDefinition {
332    create_core_type("Unit", None, None, CoreLibPointerId::Unit)
333}
334
335pub fn never() -> CoreLibTypeDefinition {
336    create_core_type("never", None, None, CoreLibPointerId::Never)
337}
338
339pub fn unknown() -> CoreLibTypeDefinition {
340    create_core_type("unknown", None, None, CoreLibPointerId::Unknown)
341}
342
343pub fn boolean() -> CoreLibTypeDefinition {
344    create_core_type("boolean", None, None, CoreLibPointerId::Boolean)
345}
346
347pub fn decimal() -> CoreLibTypeDefinition {
348    create_core_type("decimal", None, None, CoreLibPointerId::Decimal(None))
349}
350
351pub fn callable() -> CoreLibTypeDefinition {
352    create_core_type("Callable", None, None, CoreLibPointerId::Callable)
353}
354
355pub fn decimal_variant(
356    base_type: Type,
357    variant: DecimalTypeVariant,
358) -> CoreLibTypeDefinition {
359    let variant_name = variant.as_ref().to_string();
360    create_core_type(
361        "decimal",
362        Some(variant_name),
363        Some(base_type),
364        CoreLibPointerId::Decimal(Some(variant)),
365    )
366}
367pub fn endpoint() -> CoreLibTypeDefinition {
368    create_core_type("endpoint", None, None, CoreLibPointerId::Endpoint)
369}
370
371pub fn text() -> CoreLibTypeDefinition {
372    create_core_type("text", None, None, CoreLibPointerId::Text)
373}
374
375pub fn integer() -> CoreLibTypeDefinition {
376    create_core_type("integer", None, None, CoreLibPointerId::Integer(None))
377}
378
379pub fn integer_variant(
380    base_type: Type,
381    variant: IntegerTypeVariant,
382) -> CoreLibTypeDefinition {
383    let variant_name = variant.as_ref().to_string();
384    create_core_type(
385        "integer",
386        Some(variant_name),
387        Some(base_type),
388        CoreLibPointerId::Integer(Some(variant)),
389    )
390}
391
392pub fn print() -> (CoreLibPointerId, ValueContainer) {
393    (
394        CoreLibPointerId::Print,
395        ValueContainer::Value(Value::callable(
396            Some("print".to_string()),
397            CallableSignature {
398                kind: CallableKind::Function,
399                parameter_types: vec![],
400                rest_parameter_type: Some((
401                    Some("values".to_string()),
402                    Box::new(Type::unknown()),
403                )),
404                return_type: None,
405                yeet_type: None,
406            },
407            CallableBody::Native(|mut args: &[ValueContainer]| {
408                // TODO #680: add I/O abstraction layer / interface
409
410                let mut output = String::new();
411
412                // if first argument is a string value, print it directly
413                if let Some(ValueContainer::Value(Value {
414                    inner: CoreValue::Text(text),
415                    ..
416                })) = args.get(0)
417                {
418                    output.push_str(&text.0);
419                    // remove first argument from args
420                    args = &args[1..];
421                    // if there are still arguments, add a space
422                    if !args.is_empty() {
423                        output.push(' ');
424                    }
425                }
426
427                #[cfg(feature = "decompiler")]
428                let args_string = args
429                    .iter()
430                    .map(|v| {
431                        crate::decompiler::decompile_value(
432                            v,
433                            crate::decompiler::DecompileOptions::colorized(),
434                        )
435                    })
436                    .collect::<Vec<_>>()
437                    .join(" ");
438                #[cfg(not(feature = "decompiler"))]
439                let args_string = args
440                    .iter()
441                    .map(|v| v.to_string())
442                    .collect::<Vec<_>>()
443                    .join(" ");
444                output.push_str(&args_string);
445
446                #[cfg(feature = "std")]
447                println!("[PRINT] {}", output);
448                info!("[PRINT] {}", output);
449                Ok(None)
450            }),
451        )),
452    )
453}
454
455/// Creates a core type with the given parameters.
456fn create_core_type(
457    name: &str,
458    variant: Option<String>,
459    base_type: Option<Type>,
460    pointer_id: CoreLibPointerId,
461) -> CoreLibTypeDefinition {
462    let base_type_ref = match base_type {
463        Some(Type {
464            type_definition: TypeDefinition::Reference(reference),
465            ..
466        }) => Some(reference),
467        Some(_) => {
468            core::panic!("Base type must be a Reference")
469        }
470        None => None,
471    };
472    (
473        pointer_id.clone(),
474        Type::new(
475            TypeDefinition::reference(Rc::new(RefCell::new(TypeReference {
476                nominal_type_declaration: Some(NominalTypeDeclaration {
477                    name: name.to_string(),
478                    variant,
479                }),
480                type_value: Type {
481                    base_type: base_type_ref,
482                    reference_mutability: None,
483                    type_definition: TypeDefinition::Unit,
484                },
485                pointer_address: Some(PointerAddress::from(pointer_id)),
486            }))),
487            None,
488        ),
489    )
490}
491
492#[cfg(test)]
493mod tests {
494    use crate::values::core_values::endpoint::Endpoint;
495
496    use super::*;
497    use crate::stdlib::{assert_matches::assert_matches, str::FromStr};
498    use itertools::Itertools;
499
500    #[test]
501    fn core_lib() {
502        assert!(has_core_lib_type(CoreLibPointerId::Endpoint));
503        assert!(has_core_lib_type(CoreLibPointerId::Null));
504        assert!(has_core_lib_type(CoreLibPointerId::Boolean));
505        assert!(has_core_lib_type(CoreLibPointerId::Integer(None)));
506        assert!(has_core_lib_type(CoreLibPointerId::Decimal(None)));
507        assert!(has_core_lib_type(CoreLibPointerId::Type));
508        assert!(has_core_lib_type(CoreLibPointerId::Text));
509        assert!(has_core_lib_type(CoreLibPointerId::List));
510        assert!(has_core_lib_type(CoreLibPointerId::Map));
511        assert!(has_core_lib_type(CoreLibPointerId::Callable));
512        assert!(has_core_lib_type(CoreLibPointerId::Unit));
513        assert!(has_core_lib_type(CoreLibPointerId::Never));
514        assert!(has_core_lib_type(CoreLibPointerId::Unknown));
515        for variant in IntegerTypeVariant::iter() {
516            assert!(has_core_lib_type(CoreLibPointerId::Integer(Some(
517                variant
518            ))));
519        }
520        for variant in DecimalTypeVariant::iter() {
521            assert!(has_core_lib_type(CoreLibPointerId::Decimal(Some(
522                variant
523            ))));
524        }
525    }
526
527    #[test]
528    fn debug() {
529        let mut memory = Memory::new(Endpoint::LOCAL);
530        load_core_lib(&mut memory);
531        println!(
532            "{}",
533            memory
534                .get_value_reference(&CoreLibPointerId::Core.into())
535                .unwrap()
536                .borrow()
537                .value_container
538        );
539    }
540
541    #[test]
542    fn core_lib_type_addresses() {
543        let integer_base = "integer";
544        let integer_u8 = "integer/u8";
545        let integer_i32 = "integer/i32";
546        let decimal_base = "decimal";
547        let decimal_f64 = "decimal/f64";
548
549        assert_eq!(
550            CoreLibPointerId::from_str(integer_base),
551            Ok(CoreLibPointerId::Integer(None))
552        );
553        assert_eq!(
554            CoreLibPointerId::from_str(integer_u8),
555            Ok(CoreLibPointerId::Integer(Some(IntegerTypeVariant::U8)))
556        );
557        assert_eq!(
558            CoreLibPointerId::from_str(integer_i32),
559            Ok(CoreLibPointerId::Integer(Some(IntegerTypeVariant::I32)))
560        );
561        assert_eq!(
562            CoreLibPointerId::from_str(decimal_base),
563            Ok(CoreLibPointerId::Decimal(None))
564        );
565        assert_eq!(
566            CoreLibPointerId::from_str(decimal_f64),
567            Ok(CoreLibPointerId::Decimal(Some(DecimalTypeVariant::F64)))
568        );
569
570        assert_eq!(CoreLibPointerId::Integer(None).to_string(), integer_base);
571        assert_eq!(
572            CoreLibPointerId::Integer(Some(IntegerTypeVariant::U8)).to_string(),
573            integer_u8
574        );
575        assert_eq!(
576            CoreLibPointerId::Integer(Some(IntegerTypeVariant::I32))
577                .to_string(),
578            integer_i32
579        );
580        assert_eq!(CoreLibPointerId::Decimal(None).to_string(), decimal_base);
581        assert_eq!(
582            CoreLibPointerId::Decimal(Some(DecimalTypeVariant::F64))
583                .to_string(),
584            decimal_f64
585        );
586    }
587
588    #[test]
589    fn core_lib_pointer_id_conversion() {
590        let core_id = CoreLibPointerId::Core;
591        let pointer_address: PointerAddress = core_id.clone().into();
592        let converted_id: CoreLibPointerId =
593            (&pointer_address).try_into().unwrap();
594        assert_eq!(core_id, converted_id);
595
596        let boolean_id = CoreLibPointerId::Boolean;
597        let pointer_address: PointerAddress = boolean_id.clone().into();
598        let converted_id: CoreLibPointerId =
599            (&pointer_address).try_into().unwrap();
600        assert_eq!(boolean_id, converted_id);
601
602        let integer_id =
603            CoreLibPointerId::Integer(Some(IntegerTypeVariant::I32));
604        let pointer_address: PointerAddress = integer_id.clone().into();
605        let converted_id: CoreLibPointerId =
606            (&pointer_address).try_into().unwrap();
607        assert_eq!(integer_id, converted_id);
608
609        let decimal_id =
610            CoreLibPointerId::Decimal(Some(DecimalTypeVariant::F64));
611        let pointer_address: PointerAddress = decimal_id.clone().into();
612        let converted_id: CoreLibPointerId =
613            (&pointer_address).try_into().unwrap();
614        assert_eq!(decimal_id, converted_id);
615
616        let type_id = CoreLibPointerId::Type;
617        let pointer_address: PointerAddress = type_id.clone().into();
618        let converted_id: CoreLibPointerId =
619            (&pointer_address).try_into().unwrap();
620        assert_eq!(type_id, converted_id);
621    }
622
623    #[test]
624    fn base_type_simple() {
625        // integer -> integer -> integer ...
626        let integer_type = get_core_lib_type(CoreLibPointerId::Integer(None));
627        let integer_base = integer_type.base_type_reference();
628        assert_eq!(integer_base.unwrap().borrow().to_string(), "integer");
629    }
630
631    #[test]
632    fn base_type_complex() {
633        // integer/u8 -> integer -> integer -> integer ...
634        let integer_u8_type = get_core_lib_type(CoreLibPointerId::Integer(
635            Some(IntegerTypeVariant::U8),
636        ));
637        assert_eq!(integer_u8_type.to_string(), "integer/u8");
638
639        let integer = integer_u8_type.base_type_reference();
640        assert_eq!(integer.unwrap().borrow().to_string(), "integer");
641    }
642
643    #[ignore]
644    #[test]
645    fn print_core_lib_addresses_as_hex() {
646        with_full_core_lib(|core_lib_types, _| {
647            let sorted_entries = core_lib_types
648                .keys()
649                .map(|k| (k.clone(), PointerAddress::from(k.clone())))
650                .sorted_by_key(|(_, address)| address.bytes().to_vec())
651                .collect::<Vec<_>>();
652            for (core_lib_id, address) in sorted_entries {
653                println!("{:?}: {}", core_lib_id, address);
654            }
655        });
656    }
657
658    #[test]
659    #[ignore]
660    /// Generates a TypeScript mapping of core type addresses to their names.
661    /// Run this test and copy the output into `src/dif/definitions.ts`.
662    ///
663    /// `cargo test create_core_type_ts_mapping -- --show-output --ignored`
664    fn create_core_type_ts_mapping() {
665        let core_lib = create_core_lib_types();
666        let mut core_lib: Vec<(CoreLibPointerId, PointerAddress)> = core_lib
667            .keys()
668            .map(|key| (key.clone(), PointerAddress::from(key.clone())))
669            .collect();
670        core_lib.sort_by_key(|(key, _)| {
671            PointerAddress::from(key.clone()).bytes().to_vec()
672        });
673
674        println!("export const CoreTypeAddress = {{");
675        for (core_lib_id, address) in core_lib {
676            println!(
677                "    {}: \"{}\",",
678                core_lib_id.to_string().replace("/", "_"),
679                address.to_string().strip_prefix("$").unwrap()
680            );
681        }
682        println!("}} as const;");
683    }
684}