• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mattwparas / steel / 15223567548

24 May 2025 04:45AM UTC coverage: 45.922% (+0.003%) from 45.919%
15223567548

Pull #399

github

web-flow
Merge a84526a96 into 64cb606af
Pull Request #399: Use xdg home as default for searching for steel home

0 of 23 new or added lines in 2 files covered. (0.0%)

20 existing lines in 5 files now uncovered.

12505 of 27231 relevant lines covered (45.92%)

418383.87 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

63.98
/crates/steel-core/src/compiler/modules.rs
1
use crate::{
2
    compiler::{passes::VisitorMutRefUnit, program::PROVIDE},
3
    parser::{
4
        ast::{Atom, Begin, Define, ExprKind, List, Quote},
5
        expand_visitor::{expand_kernel_in_env, expand_kernel_in_env_with_change},
6
        interner::InternedString,
7
        kernel::Kernel,
8
        parser::{
9
            lower_entire_ast, lower_macro_and_require_definitions, ParseError, Parser, Sources,
10
            SyntaxObject,
11
        },
12
        tokens::TokenType,
13
    },
14
    steel_vm::{
15
        engine::{default_prelude_macros, ModuleContainer},
16
        transducers::interleave,
17
    },
18
};
19
use crate::{parser::expand_visitor::Expander, rvals::Result};
20

21
use compact_str::CompactString;
22
use fxhash::{FxHashMap, FxHashSet};
23
use once_cell::sync::Lazy;
24
// use smallvec::SmallVec;
25
use steel_parser::{ast::PROTO_HASH_GET, expr_list, parser::SourceId, span::Span};
26

27
use std::{
28
    borrow::Cow,
29
    collections::{HashMap, HashSet},
30
    io::Read,
31
    path::PathBuf,
32
    sync::Arc,
33
};
34

35
use crate::parser::expander::SteelMacro;
36
use crate::stop;
37

38
use std::time::SystemTime;
39

40
use crate::parser::expand_visitor::{expand, extract_macro_defs};
41

42
use super::{
43
    compiler::KernelDefMacroSpec,
44
    passes::{
45
        analysis::is_a_builtin_definition,
46
        begin::FlattenBegin,
47
        mangle::{collect_globals, NameMangler},
48
    },
49
    program::{FOR_SYNTAX, ONLY_IN, PREFIX_IN, REQUIRE_IDENT_SPEC},
50
};
51

52
macro_rules! time {
53
    ($label:expr, $e:expr) => {{
54
        #[cfg(feature = "profiling")]
55
        let now = std::time::Instant::now();
56

57
        let e = $e;
58

59
        #[cfg(feature = "profiling")]
60
        log::debug!(target: "pipeline_time", "{}: {:?}", $label, now.elapsed());
61

62
        e
63
    }};
64
}
65

66
macro_rules! declare_builtins {
67
    ( $( $name:expr => $path:expr ), *) => {
68
        static BUILT_INS: &[(&str, &str)] = &[
69
            $( ($name, include_str!($path)), )*
70
        ];
71

72
        pub(crate) fn intern_modules() {
3✔
73
            $(
3✔
74
                let _ = InternedString::from($name);
3✔
75
                let _ = InternedString::from($path);
3✔
76
            )*
3✔
77
        }
78
    };
79
}
80

81
pub(crate) const MANGLER_SEPARATOR: &str = "__%#__";
82

83
macro_rules! create_prelude {
84
    (
85
        $( $module:literal, )*
86
        $( for_syntax $module_for_syntax:literal ),*
87
    ) => {
88

89
        pub static PRELUDE_WITHOUT_BASE: &str = concat!(
90
            $( "(require \"", $module, "\")\n", )*
91
            $( "(require (for-syntax \"", $module_for_syntax, "\"))\n", )*
92
        );
93

94
        pub static PRELUDE_STRING: &str = concat!(
95
            "(require-builtin steel/base)\n",
96
            $( "(require \"", $module, "\")\n", )*
97
            $( "(require (for-syntax \"", $module_for_syntax, "\"))\n", )*
98
        );
99
    }
100
}
101

102
// TODO: These need to be set up and interned in a stable position.
103
declare_builtins!(
104
    "steel/option" => "../scheme/modules/option.scm",
105
    "steel/result" => "../scheme/modules/result.scm",
106
    "steel/iterators" => "../scheme/modules/iterators.scm",
107
    "steel/mutable-vectors" => "../scheme/modules/mvector.scm",
108
    "steel/async" => "../scheme/modules/async.scm",
109
    "steel/sync" => "../scheme/modules/sync.scm",
110
    "#%private/steel/contract" => "../scheme/modules/contracts.scm",
111
    "#%private/steel/print" => "../scheme/print.scm",
112
    "#%private/steel/ports" => "../scheme/modules/ports.scm",
113
    "#%private/steel/control" => "../scheme/modules/parameters.scm",
114
    "#%private/steel/reader" => "../scheme/modules/reader.scm",
115
    "#%private/steel/stdlib" => "../scheme/stdlib.scm",
116
    "#%private/steel/match" => "../scheme/modules/match.scm"
117
);
118

119
create_prelude!(
120
    "#%private/steel/control",
121
    "#%private/steel/contract",
122
    "#%private/steel/print",
123
    "#%private/steel/ports",
124
    "#%private/steel/reader",
125
    "#%private/steel/match",
126
    for_syntax "#%private/steel/control",
127
    for_syntax "#%private/steel/contract"
128
);
129

130
#[cfg(not(target_arch = "wasm32"))]
131
pub static STEEL_HOME: Lazy<Option<String>> = Lazy::new(|| {
4✔
132
    std::env::var("STEEL_HOME").ok().or_else(|| {
4✔
NEW
133
        let home = env_home::env_home_dir().map(|x| x.join(".steel"));
×
134

NEW
135
        if let Some(home) = home {
×
136
            if home.exists() {
NEW
137
                return Some(home.into_os_string().into_string().unwrap());
×
138
            }
139

140
            #[cfg(target_os = "windows")]
141
            {
142
                if let Err(_) = std::fs::create_dir(&x) {
143
                    eprintln!("Unable to create steel home directory {:?}", x)
144
                }
145

146
                return Some(home.into_os_string().into_string().unwrap());
147
            }
148
        }
149

150
        #[cfg(not(target_os = "windows"))]
151
        {
NEW
152
            let bd = xdg::BaseDirectories::new();
×
NEW
153
            let home = bd.data_home;
×
154

NEW
155
            home.map(|mut x: PathBuf| {
×
NEW
156
                x.push("steel");
×
157

158
                // Just go ahead and initialize the directory, even though
159
                // this is probably not the best place to do this. This almost
160
                // assuredly could be lifted out of this check since failing here
161
                // could cause some annoyance.
NEW
162
                if !x.exists() {
×
NEW
163
                    if let Err(_) = std::fs::create_dir(&x) {
×
NEW
164
                        eprintln!("Unable to create steel home directory {:?}", x)
×
165
                    }
166
                }
167

NEW
168
                x.into_os_string().into_string().unwrap()
×
169
            })
170
        }
171

172
        #[cfg(target_os = "windows")]
173
        None
174
    })
175
});
176

177
#[cfg(target_arch = "wasm32")]
178
pub static STEEL_HOME: Lazy<Option<String>> = Lazy::new(|| None);
179

180
pub fn steel_home() -> Option<String> {
10✔
181
    STEEL_HOME.clone()
10✔
182
}
183

184
/// Manages the modules
185
/// keeps some visited state on the manager for traversal
186
/// Also keeps track of the metadata for each file in order to determine
187
/// if it needs to be recompiled
188
#[derive(Clone)]
189
pub(crate) struct ModuleManager {
190
    compiled_modules: FxHashMap<PathBuf, CompiledModule>,
191
    file_metadata: crate::HashMap<PathBuf, SystemTime>,
192
    visited: FxHashSet<PathBuf>,
193
    custom_builtins: HashMap<String, String>,
194
    rollback_metadata: crate::HashMap<PathBuf, SystemTime>,
195
    // #[serde(skip_serializing, skip_deserializing)]
196
    module_resolvers: Vec<Arc<dyn SourceModuleResolver>>,
197
}
198

199
pub trait SourceModuleResolver: Send + Sync {
200
    fn resolve(&self, key: &str) -> Option<String>;
201
    fn exists(&self, key: &str) -> bool;
202
}
203

204
impl ModuleManager {
205
    pub(crate) fn new(
8✔
206
        compiled_modules: FxHashMap<PathBuf, CompiledModule>,
207
        file_metadata: crate::HashMap<PathBuf, SystemTime>,
208
    ) -> Self {
209
        ModuleManager {
210
            compiled_modules,
211
            file_metadata,
212
            visited: FxHashSet::default(),
8✔
213
            custom_builtins: HashMap::new(),
8✔
214
            rollback_metadata: crate::HashMap::new(),
8✔
215
            module_resolvers: Vec::new(),
8✔
216
        }
217
    }
218

219
    pub fn add_builtin_module(&mut self, module_name: String, mut text: String) {
×
220
        // Custom external builtins should be loaded with the prelude first, otherwise
221
        // they'll need to handle a bunch of imports
222
        text.insert_str(0, PRELUDE_STRING);
×
223

224
        self.custom_builtins.insert(module_name, text);
×
225
    }
226

227
    pub fn modules(&self) -> &FxHashMap<PathBuf, CompiledModule> {
88✔
228
        &self.compiled_modules
88✔
229
    }
230

231
    pub fn modules_mut(&mut self) -> &mut FxHashMap<PathBuf, CompiledModule> {
1,302✔
232
        &mut self.compiled_modules
1,302✔
233
    }
234

235
    pub(crate) fn default() -> Self {
8✔
236
        Self::new(FxHashMap::default(), crate::HashMap::default())
8✔
237
    }
238

239
    pub(crate) fn register_module_resolver(
×
240
        &mut self,
241
        resolver: impl SourceModuleResolver + 'static,
242
    ) {
243
        self.module_resolvers.push(Arc::new(resolver));
×
244
    }
245

246
    // Add the module directly to the compiled module cache
247
    pub(crate) fn add_module(
×
248
        &mut self,
249
        path: PathBuf,
250
        global_macro_map: &mut FxHashMap<InternedString, SteelMacro>,
251
        kernel: &mut Option<Kernel>,
252
        sources: &mut Sources,
253
        builtin_modules: ModuleContainer,
254
    ) -> Result<()> {
255
        self.visited.clear();
×
256

257
        // TODO: Expand macros on the fly when visiting a module. Don't wait till the end
258
        // Macro expansion should happen as we enter a module.
259
        let mut module_builder = ModuleBuilder::new_from_path(
260
            path,
×
261
            &mut self.compiled_modules,
×
262
            &mut self.visited,
×
263
            &mut self.file_metadata,
×
264
            sources,
×
265
            kernel,
×
266
            builtin_modules,
×
267
            global_macro_map,
×
268
            &self.custom_builtins,
×
269
            &[],
×
270
            &self.module_resolvers,
×
271
        )?;
272

273
        module_builder.compile()?;
×
274

275
        Ok(())
×
276
    }
277

278
    pub(crate) fn rollback_metadata(&mut self) {
22✔
279
        self.file_metadata = self.rollback_metadata.clone();
22✔
280
    }
281

282
    // #[allow(unused)]
283
    pub(crate) fn compile_main(
1,326✔
284
        &mut self,
285
        global_macro_map: &mut FxHashMap<InternedString, SteelMacro>,
286
        kernel: &mut Option<Kernel>,
287
        sources: &mut Sources,
288
        mut exprs: Vec<ExprKind>,
289
        path: Option<PathBuf>,
290
        builtin_modules: ModuleContainer,
291
        lifted_kernel_environments: &mut HashMap<String, KernelDefMacroSpec>,
292
        lifted_macro_environments: &mut HashSet<PathBuf>,
293
        search_dirs: &[PathBuf],
294
    ) -> Result<Vec<ExprKind>> {
295
        // Wipe the visited set on entry
296
        self.visited.clear();
1,326✔
297

298
        self.rollback_metadata = self.file_metadata.clone();
1,326✔
299

300
        // TODO
301
        // This is also explicitly wrong -> we should separate the global macro map from the macros found locally in this module
302
        // For instance, (cond) is global, but (define-syntax blagh) might be local to main
303
        // if a module then defines a function (blagh) that is used inside its scope, this would expand the macro in that scope
304
        // which we do not want
305
        extract_macro_defs(&mut exprs, global_macro_map)?;
1,334✔
306

307
        let mut module_builder = ModuleBuilder::main(
308
            path,
1,318✔
309
            exprs,
1,318✔
310
            &mut self.compiled_modules,
1,318✔
311
            &mut self.visited,
1,318✔
312
            &mut self.file_metadata,
1,318✔
313
            sources,
1,318✔
314
            kernel,
1,318✔
315
            builtin_modules,
1,318✔
316
            global_macro_map,
1,318✔
317
            &self.custom_builtins,
1,318✔
318
            search_dirs,
1,318✔
319
            &self.module_resolvers,
1,318✔
320
        )?;
321

322
        let mut module_statements = module_builder.compile()?;
1,318✔
323

324
        module_builder.collect_provides()?;
×
325

326
        let mut ast = module_builder.source_ast;
1,318✔
327

328
        let mut require_defines = Vec::new();
1,318✔
329

330
        let mut explicit_requires = HashMap::new();
1,318✔
331

332
        for require_object in &module_builder.require_objects {
4,046✔
333
            let path = require_object.path.get_path();
1,364✔
334
            explicit_requires.clear();
1,364✔
335

336
            // If there _are_ explicit identifiers to import, limit what we import to what
337
            // is in the set
338
            for ident in &require_object.idents_to_import {
1,382✔
339
                match ident {
340
                    MaybeRenamed::Normal(i) => {
7✔
341
                        explicit_requires.insert(i.atom_identifier().unwrap().clone(), None);
7✔
342
                    }
343

344
                    MaybeRenamed::Renamed(from, to) => {
2✔
345
                        explicit_requires.insert(
2✔
346
                            from.atom_identifier().unwrap().clone(),
2✔
347
                            Some(to.atom_identifier().unwrap().clone()),
2✔
348
                        );
349
                    }
350
                }
351
            }
352

353
            // println!("{:?}", path);
354
            let module = if let Some(module) = module_builder.compiled_modules.get(path.as_ref()) {
2,728✔
355
                module
1,364✔
356
            } else {
357
                // log::debug!(target: "modules", "No provides found for module, skipping: {:?}", path);
358

359
                continue;
×
360
            };
361

362
            for provide_expr in &module.provides {
6,064✔
363
                // For whatever reason, the value coming into module.provides is an expression like: (provide expr...)
364
                for provide in &provide_expr.list().unwrap().args[1..] {
15,298✔
365
                    let other_module_prefix = module.prefix();
366

367
                    // TODO: Expand the contract out into something we expect
368
                    // Otherwise, this is going to blow up
369
                    match provide {
370
                        ExprKind::List(l) => {
26✔
371
                            if let Some(qualifier) = l.first_ident() {
52✔
372
                                match *qualifier {
373
                                    x if x == *REQUIRE_IDENT_SPEC => {
26✔
374
                                        // Directly expand into define/contract, but with the value just being the hash get below
375

376
                                        // (bind/c contract name 'name)
377

378
                                        let name = l.args.get(1).unwrap();
379

380
                                        if !explicit_requires.is_empty()
381
                                            && !name
9✔
382
                                                .atom_identifier()
9✔
383
                                                .map(|x| explicit_requires.contains_key(x))
18✔
384
                                                .unwrap_or_default()
385
                                        {
386
                                            continue;
6✔
387
                                        }
388

389
                                        if module
20✔
390
                                            .macro_map
20✔
391
                                            .contains_key(name.atom_identifier().unwrap())
20✔
392
                                        {
393
                                            continue;
×
394
                                        }
395

396
                                        let hash_get = expr_list![
20✔
397
                                            ExprKind::atom(*PROTO_HASH_GET),
20✔
398
                                            ExprKind::atom(
20✔
399
                                                CompactString::new(MODULE_PREFIX)
20✔
400
                                                    + &other_module_prefix
20✔
401
                                            ),
402
                                            ExprKind::Quote(Box::new(Quote::new(
20✔
403
                                                name.clone(),
20✔
404
                                                SyntaxObject::default(TokenType::Quote)
20✔
405
                                            ))),
406
                                        ];
407

408
                                        let mut owned_name = name.clone();
20✔
409

410
                                        // If we have the alias listed, we should use it
411
                                        if !explicit_requires.is_empty() {
20✔
412
                                            if let Some(alias) = explicit_requires
3✔
413
                                                .get(name.atom_identifier().unwrap())
3✔
414
                                                .copied()
415
                                                .flatten()
416
                                            {
417
                                                *owned_name.atom_identifier_mut().unwrap() =
418
                                                    alias.clone();
419
                                            }
420
                                        }
421

422
                                        if let Some(prefix) = &require_object.prefix {
3✔
423
                                            if let Some(existing) = owned_name.atom_identifier_mut()
3✔
424
                                            {
425
                                                let mut prefixed_identifier = prefix.clone();
426
                                                prefixed_identifier.push_str(existing.resolve());
427

428
                                                // Update the existing identifier to point to a new one with the prefix applied
429
                                                *existing = prefixed_identifier.into();
430
                                            }
431
                                        }
432

433
                                        let define = ExprKind::Define(Box::new(Define::new(
434
                                            owned_name,
435
                                            hash_get,
436
                                            SyntaxObject::default(TokenType::Define),
437
                                        )));
438

439
                                        require_defines.push(define);
440
                                    }
441
                                    _ => {
442
                                        stop!(TypeMismatch => format!("provide expects either an identifier, (for-syntax <ident>), or (contract/out ...), found: {}", provide))
×
443
                                    }
444
                                }
445
                            } else {
446
                                stop!(TypeMismatch => "provide expects either an identifier or a (for-syntax <ident>)")
×
447
                            }
448
                        }
449
                        ExprKind::Atom(_) => {
450
                            if !explicit_requires.is_empty()
15,272✔
451
                                && !provide
27✔
452
                                    .atom_identifier()
27✔
453
                                    .map(|x| explicit_requires.contains_key(x))
54✔
454
                                    .unwrap_or_default()
455
                            {
456
                                continue;
21✔
457
                            }
458

459
                            if module
15,251✔
460
                                .macro_map
15,251✔
461
                                .contains_key(provide.atom_identifier().unwrap())
15,251✔
462
                            {
463
                                continue;
494✔
464
                            }
465

466
                            let hash_get = expr_list![
14,757✔
467
                                ExprKind::atom(*PROTO_HASH_GET),
14,757✔
468
                                ExprKind::atom(
14,757✔
469
                                    CompactString::new(MODULE_PREFIX) + &other_module_prefix
14,757✔
470
                                ),
471
                                ExprKind::Quote(Box::new(Quote::new(
14,757✔
472
                                    provide.clone(),
14,757✔
473
                                    SyntaxObject::default(TokenType::Quote)
14,757✔
474
                                ))),
475
                            ];
476

477
                            let mut owned_provide = provide.clone();
14,757✔
478

479
                            // If we have the alias listed, we should use it
480
                            if !explicit_requires.is_empty() {
14,757✔
481
                                if let Some(alias) = explicit_requires
8✔
482
                                    .get(provide.atom_identifier().unwrap())
6✔
483
                                    .copied()
484
                                    .flatten()
485
                                {
486
                                    *owned_provide.atom_identifier_mut().unwrap() = alias.clone();
487
                                }
488
                            }
489

490
                            // TODO: If there is a prefix applied, use it here?
491
                            if let Some(prefix) = &require_object.prefix {
9✔
492
                                // println!("Found prefix: {}", prefix);
493

494
                                if let Some(existing) = owned_provide.atom_identifier_mut() {
9✔
495
                                    let mut prefixed_identifier = prefix.clone();
496
                                    prefixed_identifier.push_str(existing.resolve());
497

498
                                    // Update the existing identifier to point to a new one with the prefix applied
499
                                    *existing = prefixed_identifier.into();
500
                                }
501
                            }
502

503
                            let define = ExprKind::Define(Box::new(Define::new(
504
                                owned_provide,
505
                                hash_get,
506
                                SyntaxObject::default(TokenType::Define),
507
                            )));
508

509
                            // println!("{}", define);
510

511
                            require_defines.push(define);
512
                        }
513
                        _ => {
514
                            stop!(TypeMismatch => "provide expression needs to either be a `contract/out` form or an identifier")
×
515
                        }
516
                    }
517
                }
518
            }
519
        }
520

521
        let mut mangled_asts = Vec::with_capacity(ast.len());
1,318✔
522

523
        // TODO: Move this to the lower level as well
524
        // It seems we're only doing this expansion at the top level, but we _should_ do this at the lower level as well
525
        for require_object in module_builder.require_objects.iter()
1,364✔
526
        // .filter(|x| x.for_syntax)
527
        // .map(|x| x.path.get_path())
528
        {
529
            let require_for_syntax = require_object.path.get_path();
1,364✔
530

531
            let (module, in_scope_macros, mut name_mangler) = Self::find_in_scope_macros(
532
                &mut self.compiled_modules,
1,364✔
533
                require_for_syntax.as_ref(),
1,364✔
534
                &require_object,
1,364✔
535
                &mut mangled_asts,
1,364✔
536
            );
537

538
            // let kernel_macros_in_scope: HashSet<_> =
539
            //     module.provides_for_syntax.iter().cloned().collect();
540

541
            // let defmacros_exported: HashSet<_> = module.
542

543
            // dbg!(&kernel_macros_in_scope);
544

545
            let module_name = module.name.to_str().unwrap().to_string();
1,364✔
546

547
            if let Some(kernel) = kernel.as_mut() {
2,728✔
548
                if kernel.exported_defmacros(&module_name).is_some() {
164✔
549
                    lifted_kernel_environments.insert(
164✔
550
                        module_name.clone(),
164✔
551
                        KernelDefMacroSpec {
164✔
552
                            _env: module_name.clone(),
164✔
553
                            _exported: None,
164✔
554
                            name_mangler: name_mangler.clone(),
164✔
555
                        },
556
                    );
557
                }
558
            }
559

560
            // TODO: This isn't right - only check if there are defmacro things
561
            // that we need to lift - just check the values that are in the defmacros
562
            // environment in the kernel
563
            // if !kernel_macros_in_scope.is_empty() {
564
            //     lifted_kernel_environments.insert(
565
            //         module_name.clone(),
566
            //         KernelDefMacroSpec {
567
            //             env: module_name,
568
            //             exported: None,
569
            //             name_mangler: name_mangler.clone(),
570
            //         },
571
            //     );
572
            // }
573

574
            // let module_name = Cow::from(module.name.to_str().unwrap().to_string());
575

576
            for expr in ast.iter_mut() {
3,099✔
577
                // @matt 12/8/2023
578
                // The easiest thing to do here, is to go to the other module, and find
579
                // what defmacros have been exposed on the require for syntax. Once those
580
                // have been found, we run a pass with kernel expansion, limiting the
581
                // expander to only use the macros that we've exposed. After that,
582
                // we run the expansion again, using the full suite of defmacro capabilities.
583
                //
584
                // The question that remains - how to define the neat phases of what kinds
585
                // of macros can expand into what? Can defmacro -> syntax-rules -> defmacro?
586
                // This could eventually prove to be cumbersome, but it is still early
587
                // for defmacro. Plus, I need to create a syntax-case or syntax-parse
588
                // frontend before the defmacro style macros become too pervasive.
589
                //
590
                // TODO: Replicate this behavior over to builtin modules
591

592
                // First expand the in scope macros
593
                // These are macros
594
                let mut expander = Expander::new(&in_scope_macros);
595
                expander.expand(expr)?;
×
596
                let changed = false;
1,735✔
597

598
                // (first_round_expanded, changed) = expand_kernel_in_env_with_allowed(
599
                //     first_round_expanded,
600
                //     kernel.as_mut(),
601
                //     // We don't need to expand those here
602
                //     ModuleContainer::default(),
603
                //     module.name.to_str().unwrap().to_string(),
604
                //     &kernel_macros_in_scope,
605
                // )?;
606

607
                // If the kernel expander expanded into something - go ahead
608
                // and expand all of the macros in this
609
                // if changed || expander.changed {
610
                // Expand here?
611
                // first_round_expanded = expand(first_round_expanded, &module.macro_map)?;
612

613
                // Probably don't need this
614
                // (first_round_expanded, changed) = expand_kernel_in_env_with_change(
615
                //     first_round_expanded,
616
                //     kernel.as_mut(),
617
                //     ModuleContainer::default(),
618
                //     module.name.to_str().unwrap().to_string(),
619
                // )?;
620

621
                // This is pretty suspect, and needs to be revisited - only the output of the
622
                // macro expansion and not the whole thing needs to be mangled most likely.
623
                // Otherwise, we'll run into weird stuff?
624
                // if changed {
625
                //     name_mangler.visit(&mut first_round_expanded);
626
                // }
627
                // }
628

629
                if expander.changed || changed {
3,077✔
630
                    let _source_id = sources.get_source_id(&module.name).unwrap();
393✔
631

632
                    // let mut fully_expanded = first_round_expanded;
633

634
                    expand(
635
                        expr,
393✔
636
                        &module.macro_map,
393✔
637
                        // source_id,
638
                    )?;
639

640
                    // Expanding the kernel with only these macros...
641
                    let changed = expand_kernel_in_env_with_change(
642
                        expr,
393✔
643
                        kernel.as_mut(),
393✔
644
                        // We don't need to expand those here
645
                        ModuleContainer::default(),
393✔
646
                        &module_name,
393✔
647
                        // &kernel_macros_in_scope,
648
                    )?;
649

650
                    if changed {
×
651
                        name_mangler.visit(expr);
×
652
                    }
653

654
                    // lifted_kernel_environments.insert(
655
                    //     module_name.clone(),
656
                    //     KernelDefMacroSpec {
657
                    //         env: module_name,
658
                    //         exported: None,
659
                    //         name_mangler: name_mangler.clone(),
660
                    //     },
661
                    // );
662

663
                    // Ok(fully_expanded)
664
                }
665
                // else {
666
                //     Ok(first_round_expanded)
667
                // }
668
            }
669

670
            // ast = ast
671
            //     .into_iter()
672
            //     .map(|x| {
673
            //         // @matt 12/8/2023
674
            //         // The easiest thing to do here, is to go to the other module, and find
675
            //         // what defmacros have been exposed on the require for syntax. Once those
676
            //         // have been found, we run a pass with kernel expansion, limiting the
677
            //         // expander to only use the macros that we've exposed. After that,
678
            //         // we run the expansion again, using the full suite of defmacro capabilities.
679
            //         //
680
            //         // The question that remains - how to define the neat phases of what kinds
681
            //         // of macros can expand into what? Can defmacro -> syntax-rules -> defmacro?
682
            //         // This could eventually prove to be cumbersome, but it is still early
683
            //         // for defmacro. Plus, I need to create a syntax-case or syntax-parse
684
            //         // frontend before the defmacro style macros become too pervasive.
685
            //         //
686
            //         // TODO: Replicate this behavior over to builtin modules
687

688
            //         // First expand the in scope macros
689
            //         // These are macros
690
            //         let mut expander = Expander::new(&in_scope_macros);
691
            //         let mut first_round_expanded = expander.expand(x)?;
692
            //         let mut changed = false;
693

694
            //         // (first_round_expanded, changed) = expand_kernel_in_env_with_allowed(
695
            //         //     first_round_expanded,
696
            //         //     kernel.as_mut(),
697
            //         //     // We don't need to expand those here
698
            //         //     ModuleContainer::default(),
699
            //         //     module.name.to_str().unwrap().to_string(),
700
            //         //     &kernel_macros_in_scope,
701
            //         // )?;
702

703
            //         // If the kernel expander expanded into something - go ahead
704
            //         // and expand all of the macros in this
705
            //         // if changed || expander.changed {
706
            //         // Expand here?
707
            //         // first_round_expanded = expand(first_round_expanded, &module.macro_map)?;
708

709
            //         // Probably don't need this
710
            //         // (first_round_expanded, changed) = expand_kernel_in_env_with_change(
711
            //         //     first_round_expanded,
712
            //         //     kernel.as_mut(),
713
            //         //     ModuleContainer::default(),
714
            //         //     module.name.to_str().unwrap().to_string(),
715
            //         // )?;
716

717
            //         // This is pretty suspect, and needs to be revisited - only the output of the
718
            //         // macro expansion and not the whole thing needs to be mangled most likely.
719
            //         // Otherwise, we'll run into weird stuff?
720
            //         // if changed {
721
            //         //     name_mangler.visit(&mut first_round_expanded);
722
            //         // }
723
            //         // }
724

725
            //         if expander.changed || changed {
726
            //             let source_id = sources.get_source_id(&module.name).unwrap();
727

728
            //             let mut fully_expanded = first_round_expanded;
729

730
            //             expand(
731
            //                 &mut fully_expanded,
732
            //                 &module.macro_map,
733
            //                 // source_id,
734
            //             )?;
735

736
            //             let module_name = module.name.to_str().unwrap().to_string();
737

738
            //             // Expanding the kernel with only these macros...
739
            //             let changed = expand_kernel_in_env_with_change(
740
            //                 &mut fully_expanded,
741
            //                 kernel.as_mut(),
742
            //                 // We don't need to expand those here
743
            //                 ModuleContainer::default(),
744
            //                 module_name.clone(),
745
            //                 // &kernel_macros_in_scope,
746
            //             )?;
747

748
            //             if changed {
749
            //                 name_mangler.visit(&mut fully_expanded);
750
            //             }
751

752
            //             // lifted_kernel_environments.insert(
753
            //             //     module_name.clone(),
754
            //             //     KernelDefMacroSpec {
755
            //             //         env: module_name,
756
            //             //         exported: None,
757
            //             //         name_mangler: name_mangler.clone(),
758
            //             //     },
759
            //             // );
760

761
            //             Ok(fully_expanded)
762
            //         } else {
763
            //             Ok(first_round_expanded)
764
            //         }
765
            //     })
766
            //     .collect::<Result<_>>()?;
767

768
            // Global macro map - also need to expand with ALL macros
769
            // post expansion in the target environment, which means we can't _just_
770
            // extend the global macro map with the target in scope macros, we need to
771
            // do something like the two pass expansion
772
            global_macro_map.extend(in_scope_macros);
1,364✔
773

774
            lifted_macro_environments.insert(module.name.clone());
1,364✔
775
        }
776

777
        // Include the defines from the modules now imported
778
        module_statements.append(&mut require_defines);
1,318✔
779

780
        // The next two lines here expand _all_ of the source code with the top level macros
781
        // This is necessary because of the std library macros, although this should be able to be
782
        // solved with scoped imports of the standard library explicitly
783
        module_statements.append(&mut ast);
1,318✔
784

785
        time!("Top level macro evaluation time", {
1,312✔
786
            for expr in module_statements.iter_mut() {
30,809✔
787
                expand(expr, global_macro_map)?;
30,815✔
788
            }
789
        });
790

791
        // @Matt 7/4/23
792
        // TODO: With mangling, this could cause problems. We'll want to un-mangle quotes AFTER the macro has been expanded,
793
        // in order to preserve the existing behavior.
794
        // let result = module_statements
795
        //     .into_iter()
796
        //     .map(|x| expand(x, global_macro_map))
797
        //     .collect::<Result<_>>();
798

799
        // result
800

801
        Ok(module_statements)
802
    }
803

804
    fn find_in_scope_macros<'a>(
3,016✔
805
        compiled_modules: &'a mut FxHashMap<PathBuf, CompiledModule>,
806
        require_for_syntax: &'a PathBuf,
807
        require_object: &'a RequireObject,
808
        mangled_asts: &'a mut Vec<ExprKind>,
809
    ) -> (
810
        &'a CompiledModule,
811
        FxHashMap<InternedString, SteelMacro>,
812
        NameMangler,
813
    ) {
814
        let module = compiled_modules
3,016✔
815
            .get_mut(require_for_syntax)
3,016✔
816
            .expect(&format!("Module missing!: {:?}", require_for_syntax));
3,016✔
817

818
        let prefix = module.prefix();
3,016✔
819

820
        let globals = collect_globals(&module.ast);
3,016✔
821

822
        let mut name_mangler = NameMangler::new(globals, prefix);
3,016✔
823

824
        // If the module hasn't been emitted already, then include it here
825
        if !module.emitted {
3,016✔
826
            let mut module_ast = module.ast.clone();
×
827

828
            name_mangler.mangle_vars(&mut module_ast);
×
829

830
            mangled_asts.append(&mut module_ast);
×
831
        }
832

833
        // let provided_macros = module.provides_for
834
        // let expander = Expander::new(&module.macro_map);
835
        // TODO
836
        // expand expressions one by one
837
        // if expansion with _just_ public macros from the required module doesn't do anything, stop
838
        // if it _does_ change, do another pass with all of the macros in scope
839
        // do this for each of the expressions in the file in this loop
840
        // TODO -> try not cloning this
841
        // TODO -> do this in the module expansion as well
842
        let mut in_scope_macros = module
3,016✔
843
            .provides_for_syntax
3,016✔
844
            .iter()
845
            // Chain with just the normal provides!
846
            .filter_map(|x| {
6,520✔
847
                let smacro = Arc::make_mut(&mut module.macro_map).get_mut(x);
3,504✔
848

849
                if let Some(smacro) = smacro {
6,990✔
850
                    if !smacro.special_mangled {
×
851
                        for expr in smacro.exprs_mut() {
9,687✔
852
                            name_mangler.visit(expr);
×
853
                        }
854
                    }
855

856
                    Some((*x, smacro.clone()))
×
857
                } else {
858
                    None
18✔
859
                }
860

861
                // if !x.1.special_mangled {
862
                //     for expr in x.1.exprs_mut() {
863
                //         name_mangler.visit(expr);
864
                //     }
865
                // }
866

867
                // (x.0, x.1.clone())
868
            })
869
            .collect::<FxHashMap<_, _>>();
870

871
        // If the require_object specifically imports things, we should reference it
872

873
        if !require_object.idents_to_import.is_empty() {
3,016✔
874
            for maybe in &require_object.idents_to_import {
30✔
875
                match maybe {
×
876
                    MaybeRenamed::Normal(n) => {
11✔
877
                        if let Some(ident) = n.atom_identifier() {
11✔
878
                            if let Some(mut m) = module.macro_map.get(ident).cloned() {
×
879
                                for expr in m.exprs_mut() {
×
880
                                    name_mangler.visit(expr);
×
881
                                }
882

883
                                if let Some(prefix) = &require_object.prefix {
×
884
                                    in_scope_macros
×
885
                                        .insert((prefix.to_string() + ident.resolve()).into(), m);
×
886
                                } else {
887
                                    in_scope_macros.insert(*ident, m);
×
888
                                }
889
                            }
890
                        }
891
                    }
892
                    MaybeRenamed::Renamed(from, to) => {
2✔
893
                        if let Some(ident) = from.atom_identifier() {
4✔
894
                            if let Some(mut m) = module.macro_map.get(ident).cloned() {
×
895
                                for expr in m.exprs_mut() {
×
896
                                    name_mangler.visit(expr);
×
897
                                }
898
                                // TODO: Remove this unwrap
899
                                // in_scope_macros.insert(*to.atom_identifier().unwrap(), m);
900

901
                                if let Some(prefix) = &require_object.prefix {
×
902
                                    in_scope_macros.insert(
×
903
                                        (prefix.to_string()
×
904
                                            + to.atom_identifier().unwrap().resolve())
×
905
                                        .into(),
×
906
                                        m,
×
907
                                    );
908
                                } else {
909
                                    in_scope_macros.insert(*to.atom_identifier().unwrap(), m);
×
910
                                }
911
                            }
912
                        }
913
                    }
914
                }
915
            }
916
        } else {
917
            // Pull in all of the macros that the module exposes
918

919
            for provide_expr in &module.provides {
14,316✔
920
                if let Some(provide_expr) = provide_expr.list() {
5,652✔
921
                    for ident in provide_expr.args.split_first().unwrap().1 {
39,410✔
922
                        // println!("Looking for {}", ident);
923

924
                        if let Some(ident) = ident.atom_identifier() {
38,892✔
925
                            if let Some(mut m) = module.macro_map.get(ident).cloned() {
665✔
926
                                // println!("Pulling in macro: {}", ident);
927

928
                                for expr in m.exprs_mut() {
1,108✔
929
                                    name_mangler.visit(expr);
×
930
                                }
931

932
                                if let Some(prefix) = &require_object.prefix {
×
933
                                    in_scope_macros
×
934
                                        .insert((prefix.to_string() + ident.resolve()).into(), m);
×
935
                                } else {
936
                                    in_scope_macros.insert(*ident, m);
665✔
937
                                }
938
                            }
939
                        }
940
                    }
941
                }
942
            }
943
        }
944

945
        // Check what macros are in scope here
946
        // println!(
947
        //     "In scope macros: {:#?}",
948
        //     in_scope_macros.keys().collect::<Vec<_>>()
949
        // );
950
        (module, in_scope_macros, name_mangler)
3,016✔
951
    }
952
}
953

954
// Pre-compile module to bytecode? Is it even possible?
955
// Dynamically linking the module would then make it relatively
956
// easy to just load everything up at the start.
957
// Compiled module _should_ be possible now. Just create a target
958
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
959
pub struct CompiledModule {
960
    name: PathBuf,
961
    provides: Vec<ExprKind>,
962
    require_objects: Vec<RequireObject>,
963
    provides_for_syntax: Vec<InternedString>,
964
    pub(crate) macro_map: Arc<FxHashMap<InternedString, SteelMacro>>,
965
    ast: Vec<ExprKind>,
966
    emitted: bool,
967
    cached_prefix: CompactString,
968
    downstream: Vec<PathBuf>,
969
}
970

971
pub static MANGLER_PREFIX: &'static str = "##mm";
972
pub static MODULE_PREFIX: &'static str = "__module-";
973
pub static MANGLED_MODULE_PREFIX: &'static str = "__module-##mm";
974

975
pub fn path_to_module_name(name: PathBuf) -> String {
×
976
    let mut base = CompactString::new(MANGLED_MODULE_PREFIX);
×
977

978
    if let Some(steel_home) = STEEL_HOME.as_ref() {
×
979
        // Intern this?
980
        let name = name
×
981
            .to_str()
982
            .unwrap()
983
            .trim_start_matches(steel_home.as_str());
×
984

985
        let interned = InternedString::from_str(&name);
×
986
        let id = interned.get().into_inner();
×
987

988
        base.push_str(&id.to_string());
×
989
        base.push_str(MANGLER_SEPARATOR);
×
990
    } else {
991
        let interned = InternedString::from_str(name.to_str().unwrap());
×
992
        let id = interned.get().into_inner();
×
993

994
        base.push_str(&id.to_string());
×
995
        base.push_str(MANGLER_SEPARATOR);
×
996
    }
997

998
    base.into_string()
×
999
}
1000

1001
// TODO: @Matt 6/12/23 - This _should_ be serializable. If possible, we can try to store intermediate objects down to some file.
1002
impl CompiledModule {
1003
    pub fn new(
1,208✔
1004
        name: PathBuf,
1005
        provides: Vec<ExprKind>,
1006
        require_objects: Vec<RequireObject>,
1007
        provides_for_syntax: Vec<InternedString>,
1008
        macro_map: Arc<FxHashMap<InternedString, SteelMacro>>,
1009
        ast: Vec<ExprKind>,
1010
        downstream: Vec<PathBuf>,
1011
    ) -> Self {
1012
        let mut base = CompactString::new(MANGLER_PREFIX);
1,208✔
1013

1014
        if let Some(steel_home) = STEEL_HOME.as_ref() {
2,416✔
1015
            // Intern this?
1016
            let name = name
1017
                .to_str()
1018
                .unwrap()
1019
                .trim_start_matches(steel_home.as_str());
1020

1021
            let interned = InternedString::from_str(&name);
1022
            let id = interned.get().into_inner();
1023

1024
            // base.push_str(name);
1025
            base.push_str(&id.to_string());
1026
            base.push_str(MANGLER_SEPARATOR);
1027

1028
            // println!("{}", base);
1029
            // println!("Byte length: {}", base.len());
1030
        } else {
1031
            let interned = InternedString::from_str(name.to_str().unwrap());
×
1032
            let id = interned.get().into_inner();
×
1033

1034
            // base.push_str(self.name.to_str().unwrap());
1035
            base.push_str(&id.to_string());
×
1036
            base.push_str(MANGLER_SEPARATOR);
×
1037
        }
1038

1039
        Self {
1040
            name,
1041
            provides,
1042
            require_objects,
1043
            provides_for_syntax,
1044
            macro_map,
1045
            ast,
1046
            emitted: false,
1047
            cached_prefix: base,
1048
            downstream,
1049
        }
1050
    }
1051

1052
    // TODO: Should cache this
1053
    pub fn prefix(&self) -> CompactString {
21,174✔
1054
        self.cached_prefix.clone()
21,174✔
1055
    }
1056

1057
    pub fn get_ast(&self) -> &[ExprKind] {
×
1058
        &self.ast
×
1059
    }
1060

1061
    pub fn get_provides(&self) -> &[ExprKind] {
×
1062
        &self.provides
×
1063
    }
1064

1065
    // pub fn get_requires(&self) -> &[PathBuf] {
1066
    //     &self.requires
1067
    // }
1068

1069
    pub fn set_emitted(&mut self, emitted: bool) {
1,208✔
1070
        self.emitted = emitted;
1,208✔
1071
    }
1072

1073
    fn to_top_level_module(
1,208✔
1074
        &self,
1075
        modules: &FxHashMap<PathBuf, CompiledModule>,
1076
        global_macro_map: &FxHashMap<InternedString, SteelMacro>,
1077
    ) -> Result<ExprKind> {
1078
        let mut globals = collect_globals(&self.ast);
1,208✔
1079

1080
        let mut exprs = self.ast.clone();
1,208✔
1081

1082
        let mut provide_definitions = Vec::new();
1,208✔
1083

1084
        // TODO: Change this to not use the full path. Unfortunately that isn't portable,
1085
        // so we should use something that is more agnostic of the target location
1086
        // if we were to load.
1087
        //
1088
        // Probably a better idea would be to somehow assign a unique ID to each module;
1089
        // the path from the $STEEL_HOME would be relatively safe. That way we can just strip
1090
        // the $STEEL_HOME root away from the path if it starts with it, and then have
1091
        // that resolve to be the "name" of the module.
1092
        let prefix = self.prefix();
1,208✔
1093

1094
        // Now we should be able to set up a series of requires with the right style
1095
        // ;; Refresh the module definition in this namespace
1096
        // (define a-module.rkt-b (hash-get 'b b-module.rkt-b))
1097

1098
        let mut explicit_requires = HashMap::new();
1,208✔
1099

1100
        // TODO: This is the same as the top level, they should be merged
1101
        for require_object in &self.require_objects {
4,512✔
1102
            let path = require_object.path.get_path();
1,652✔
1103

1104
            explicit_requires.clear();
1,652✔
1105

1106
            for ident in &require_object.idents_to_import {
1,660✔
1107
                match ident {
1108
                    MaybeRenamed::Normal(i) => {
4✔
1109
                        explicit_requires.insert(i.atom_identifier().unwrap().clone(), None);
4✔
1110
                    }
1111
                    MaybeRenamed::Renamed(from, to) => {
×
1112
                        explicit_requires.insert(
×
1113
                            from.atom_identifier().unwrap().clone(),
×
1114
                            Some(to.atom_identifier().unwrap().clone()),
×
1115
                        );
1116
                    }
1117
                }
1118
            }
1119

1120
            // println!("{:?}", path);
1121
            // println!("{:?}", modules.keys().collect::<Vec<_>>());
1122
            let module = modules.get(path.as_ref()).unwrap();
1,652✔
1123

1124
            let other_module_prefix = module.prefix();
1,652✔
1125

1126
            for provide_expr in &module.provides {
8,264✔
1127
                // For whatever reason, the value coming into module.provides is an expression like: (provide expr...)
1128
                for provide in &provide_expr.list().unwrap().args[1..] {
24,155✔
1129
                    match provide {
1130
                        ExprKind::List(l) => {
501✔
1131
                            if let Some(qualifier) = l.first_ident() {
1,002✔
1132
                                if module.macro_map.contains_key(qualifier) {
1133
                                    continue;
×
1134
                                }
1135

1136
                                match *qualifier {
501✔
1137
                                    x if x == *REQUIRE_IDENT_SPEC => {
501✔
1138
                                        // Directly expand into define/contract, but with the value just being the hash get below
1139

1140
                                        // (bind/c contract name 'name)
1141

1142
                                        let name = l.args.get(1).unwrap();
1143

1144
                                        if !explicit_requires.is_empty()
1145
                                            && !name
×
1146
                                                .atom_identifier()
×
1147
                                                .map(|x| explicit_requires.contains_key(x))
×
1148
                                                .unwrap_or_default()
1149
                                        {
1150
                                            continue;
×
1151
                                        }
1152

1153
                                        if module
501✔
1154
                                            .macro_map
501✔
1155
                                            .contains_key(name.atom_identifier().unwrap())
501✔
1156
                                        {
1157
                                            continue;
×
1158
                                        }
1159

1160
                                        let hash_get = expr_list![
501✔
1161
                                            ExprKind::atom(*PROTO_HASH_GET),
501✔
1162
                                            ExprKind::atom(
501✔
1163
                                                CompactString::new(MODULE_PREFIX)
501✔
1164
                                                    + &other_module_prefix
501✔
1165
                                            ),
1166
                                            ExprKind::Quote(Box::new(Quote::new(
501✔
1167
                                                name.clone(),
501✔
1168
                                                SyntaxObject::default(TokenType::Quote)
501✔
1169
                                            ))),
1170
                                        ];
1171

1172
                                        let mut owned_name = name.clone();
501✔
1173

1174
                                        // If we have the alias listed, we should use it
1175
                                        if !explicit_requires.is_empty() {
501✔
1176
                                            if let Some(alias) = explicit_requires
×
1177
                                                .get(name.atom_identifier().unwrap())
×
1178
                                                .copied()
1179
                                                .flatten()
1180
                                            {
1181
                                                *owned_name.atom_identifier_mut().unwrap() =
1182
                                                    alias.clone();
1183
                                            }
1184
                                        }
1185

1186
                                        if let Some(prefix) = &require_object.prefix {
×
1187
                                            if let Some(existing) = owned_name.atom_identifier_mut()
×
1188
                                            {
1189
                                                let mut prefixed_identifier = prefix.clone();
1190
                                                prefixed_identifier.push_str(existing.resolve());
1191

1192
                                                // Update the existing identifier to point to a new one with the prefix applied
1193
                                                *existing = prefixed_identifier.into();
1194
                                            }
1195
                                        }
1196

1197
                                        globals.insert(*name.atom_identifier().unwrap());
1198

1199
                                        let define = ExprKind::Define(Box::new(Define::new(
1200
                                            owned_name,
1201
                                            hash_get,
1202
                                            SyntaxObject::default(TokenType::Define),
1203
                                        )));
1204

1205
                                        provide_definitions.push(define);
1206
                                    }
1207

1208
                                    // x if x == *CONTRACT_OUT => {
1209
                                    //     // Directly expand into define/contract, but with the value just being the hash get below
1210

1211
                                    //     // (bind/c contract name 'name)
1212

1213
                                    //     let mut name = l.args.get(1).unwrap().clone();
1214
                                    //     let _contract = l.args.get(2).unwrap();
1215

1216
                                    //     if !explicit_requires.is_empty()
1217
                                    //         && !name
1218
                                    //             .atom_identifier()
1219
                                    //             .map(|x| explicit_requires.contains_key(x))
1220
                                    //             .unwrap_or_default()
1221
                                    //     {
1222
                                    //         continue;
1223
                                    //     }
1224

1225
                                    //     // If we have the alias listed, we should use it
1226
                                    //     if !explicit_requires.is_empty() {
1227
                                    //         if let Some(alias) = explicit_requires
1228
                                    //             .get(name.atom_identifier().unwrap())
1229
                                    //             .copied()
1230
                                    //             .flatten()
1231
                                    //         {
1232
                                    //             *name.atom_identifier_mut().unwrap() =
1233
                                    //                 alias.clone();
1234
                                    //         }
1235
                                    //     }
1236

1237
                                    //     if let Some(prefix) = &require_object.prefix {
1238
                                    //         if let Some(existing) = name.atom_identifier_mut() {
1239
                                    //             let mut prefixed_identifier = prefix.clone();
1240
                                    //             prefixed_identifier.push_str(existing.resolve());
1241

1242
                                    //             // Update the existing identifier to point to a new one with the prefix applied
1243
                                    //             *existing = prefixed_identifier.into();
1244
                                    //         }
1245
                                    //     }
1246

1247
                                    //     // Since this is now bound to be in the scope of the current working module, we also want
1248
                                    //     // this to be mangled. In the event we do something like, qualify the import, then we might
1249
                                    //     // have to mangle this differently
1250
                                    //     globals.insert(*name.atom_identifier().unwrap());
1251

1252
                                    //     let hash_get = expr_list![
1253
                                    //         ExprKind::atom(*PROTO_HASH_GET),
1254
                                    //         ExprKind::atom(
1255
                                    //             "__module-".to_string() + &other_module_prefix
1256
                                    //         ),
1257
                                    //         ExprKind::Quote(Box::new(Quote::new(
1258
                                    //             name.clone(),
1259
                                    //             SyntaxObject::default(TokenType::Quote)
1260
                                    //         ))),
1261
                                    //     ];
1262

1263
                                    //     let define = ExprKind::Define(Box::new(Define::new(
1264
                                    //         ExprKind::atom(
1265
                                    //             prefix.clone()
1266
                                    //                 + name.atom_identifier().unwrap().resolve(),
1267
                                    //         ),
1268
                                    //         hash_get,
1269
                                    //         SyntaxObject::default(TokenType::Define),
1270
                                    //     )));
1271

1272
                                    //     provide_definitions.push(define);
1273
                                    // }
1274
                                    _ => {
1275
                                        stop!(TypeMismatch => format!("provide expects either an identifier, (for-syntax <ident>), or (contract/out ...) - found: {}", provide))
×
1276
                                    }
1277
                                }
1278
                            } else {
1279
                                stop!(TypeMismatch => "provide expects either an identifier or a (for-syntax <ident>)")
×
1280
                            }
1281
                        }
1282
                        ExprKind::Atom(_) => {
1283
                            if !explicit_requires.is_empty()
23,654✔
1284
                                && !provide
7✔
1285
                                    .atom_identifier()
7✔
1286
                                    .map(|x| explicit_requires.contains_key(x))
14✔
1287
                                    .unwrap_or_default()
1288
                            {
1289
                                continue;
3✔
1290
                            }
1291

1292
                            if module
23,651✔
1293
                                .macro_map
23,651✔
1294
                                .contains_key(provide.atom_identifier().unwrap())
23,651✔
1295
                            {
1296
                                continue;
171✔
1297
                            }
1298

1299
                            // Mangle with a prefix if necessary
1300
                            let mut provide = provide.clone();
23,480✔
1301
                            let raw_provide = provide.clone();
23,480✔
1302

1303
                            // If we have the alias listed, we should use it
1304
                            if !explicit_requires.is_empty() {
23,480✔
1305
                                if let Some(alias) = explicit_requires
4✔
1306
                                    .get(provide.atom_identifier().unwrap())
4✔
1307
                                    .copied()
1308
                                    .flatten()
1309
                                {
1310
                                    *provide.atom_identifier_mut().unwrap() = alias.clone();
1311
                                }
1312
                            }
1313

1314
                            if let Some(prefix) = &require_object.prefix {
×
1315
                                if let Some(existing) = provide.atom_identifier_mut() {
×
1316
                                    let mut prefixed_identifier = prefix.clone();
1317
                                    prefixed_identifier.push_str(existing.resolve());
1318

1319
                                    // Update the existing identifier to point to a new one with the prefix applied
1320
                                    *existing = prefixed_identifier.into();
1321
                                }
1322
                            }
1323

1324
                            let provide_ident = provide.atom_identifier().unwrap();
1325

1326
                            // Since this is now bound to be in the scope of the current working module, we also want
1327
                            // this to be mangled. In the event we do something like, qualify the import, then we might
1328
                            // have to mangle this differently
1329
                            globals.insert(*provide_ident);
1330

1331
                            let define = ExprKind::Define(Box::new(Define::new(
1332
                                ExprKind::atom(prefix.clone() + provide_ident.resolve()),
1333
                                expr_list![
1334
                                    ExprKind::atom(*PROTO_HASH_GET),
1335
                                    ExprKind::atom(
1336
                                        CompactString::new(MODULE_PREFIX) + &other_module_prefix
1337
                                    ),
1338
                                    ExprKind::Quote(Box::new(Quote::new(
1339
                                        raw_provide.clone(),
1340
                                        SyntaxObject::default(TokenType::Quote)
1341
                                    )))
1342
                                ],
1343
                                SyntaxObject::default(TokenType::Define),
1344
                            )));
1345

1346
                            // if require_object.prefix.is_some() {
1347
                            //     println!("{}", define);
1348
                            // }
1349

1350
                            provide_definitions.push(define);
1351
                        }
1352
                        _ => {
1353
                            stop!(TypeMismatch => "provide expression needs to either be a `contract/out` form or an identifier")
×
1354
                        }
1355
                    }
1356
                }
1357
            }
1358
        }
1359

1360
        // Mangle all of the variables that are either:
1361
        // 1. Defined locally in this file
1362
        // 2. Required by another file
1363
        let mut name_mangler = NameMangler::new(globals, prefix.clone());
1,208✔
1364

1365
        // Afterwards, walk through and unmangle any quoted values, since these
1366
        // were intended to be used with non mangled values.
1367
        // let name_unmangler = NameUnMangler::new(&prefix);
1368

1369
        name_mangler.mangle_vars(&mut exprs);
1,208✔
1370

1371
        // The provide definitions should also be mangled
1372
        name_mangler.mangle_vars(&mut provide_definitions);
1,208✔
1373

1374
        // let mut hash_builder = Vec::new();
1375

1376
        // These are gonna be the pairs
1377
        // hash_builder.push(());
1378

1379
        // Construct the series of provides as well, we'll want these to refer to the correct values
1380
        //
1381
        let mut provides: smallvec::SmallVec<[(ExprKind, ExprKind); 24]> = self
1,208✔
1382
            .provides
1,208✔
1383
            .iter()
1384
            .flat_map(|x| &x.list().unwrap().args[1..])
2,910✔
1385
            .cloned()
1386
            .map(|x| (x.clone(), x))
11,220✔
1387
            .collect();
1388

1389
        for provide in &mut provides {
23,648✔
1390
            match &provide.1 {
11,220✔
1391
                ExprKind::List(l) => {
503✔
1392
                    if let Some(qualifier) = l.first_ident() {
1,006✔
1393
                        match qualifier {
1394
                            x if *x == *REQUIRE_IDENT_SPEC => {
503✔
1395
                                // *provide = expand(l.get(2).unwrap().clone(), global_macro_map)?;
1396

1397
                                // *provide = expand(l.)
1398

1399
                                provide.0 = l.get(1).unwrap().clone();
503✔
1400

1401
                                let mut provide_expr = l.get(2).unwrap().clone();
503✔
1402
                                expand(&mut provide_expr, global_macro_map)?;
503✔
1403

1404
                                provide.1 = provide_expr;
503✔
1405

1406
                                continue;
503✔
1407

1408
                                // name_unmangler.unmangle_expr(provide);
1409
                            }
1410
                            // x if *x == *CONTRACT_OUT => {
1411
                            //     // Update the item to point to just the name
1412
                            //     //
1413
                            //     // *provide = l.get(1).unwrap().clone();
1414
                            //     // {
1415
                            //     //     println!("---------");
1416
                            //     //     println!("Provide expr: {}", l.to_string());
1417
                            //     // }
1418

1419
                            //     provide.0 = l.get(1).unwrap().clone();
1420

1421
                            //     let mut provide_expr = expr_list![
1422
                            //         ExprKind::ident("bind/c"),
1423
                            //         l.get(2).unwrap().clone(),
1424
                            //         l.get(1).unwrap().clone(),
1425
                            //         ExprKind::Quote(Box::new(Quote::new(
1426
                            //             l.get(1).unwrap().clone(),
1427
                            //             SyntaxObject::default(TokenType::Quote)
1428
                            //         ))),
1429
                            //     ];
1430

1431
                            //     expand(&mut provide_expr, global_macro_map)?;
1432

1433
                            //     provide.1 = provide_expr;
1434

1435
                            //     name_unmangler.unmangle_expr(&mut provide.1);
1436
                            //     // continue;
1437
                            // }
1438
                            unknown => {
×
1439
                                stop!(TypeMismatch => "bar provide expects either an identifier, (for-syntax <ident>), or (contract/out ...), found: {}", unknown)
×
1440
                            }
1441
                        }
1442
                    } else {
1443
                        stop!(TypeMismatch => "provide expects either an identifier or a (for-syntax <ident>)")
×
1444
                    }
1445
                }
1446
                ExprKind::Atom(_) => {
1447
                    continue;
10,717✔
1448
                }
1449
                _ => {
1450
                    stop!(TypeMismatch => "provide expression needs to either be a `contract/out` form or an identifier")
×
1451
                }
1452
            }
1453
        }
1454

1455
        // Drop all of the macro references here
1456
        provides.retain(|x| !self.macro_map.contains_key(x.0.atom_identifier().unwrap()));
12,428✔
1457

1458
        // We want one without the mangled version, for the actual provides
1459
        let un_mangled = provides.clone();
1460

1461
        let left_unmangled: Vec<_> = un_mangled.into_iter().map(|x| x.0).collect();
10,726✔
1462

1463
        let mut right: Vec<_> = provides.into_iter().map(|x| x.1).collect();
10,726✔
1464

1465
        name_mangler.mangle_vars(&mut right);
1466
        // name_unmangler.unmangle_vars(&mut provides);
1467

1468
        let mut hash_body = vec![ExprKind::ident("hash")];
1469

1470
        // We can put the module name in there, but that doesn't help us get the docs out...
1471
        // Probably need to just store the docs directly in the module itself as well?
1472
        // hash_body.push(ExprKind::atom("#:module-name"));
1473
        // hash_body.push(ExprKind::atom(prefix.clone()));
1474

1475
        // left_unmangled.pretty_print();
1476

1477
        hash_body.extend(interleave(
1478
            left_unmangled.into_iter().map(|x| {
10,726✔
1479
                if let ExprKind::Atom(_) = x {
10,726✔
1480
                    ExprKind::Quote(Box::new(Quote::new(
10,726✔
1481
                        x,
10,726✔
1482
                        SyntaxObject::default(TokenType::Quote),
10,726✔
1483
                    )))
1484
                } else if let ExprKind::List(l) = x {
×
1485
                    if let Some(qualifier) = l.first_ident() {
×
1486
                        match qualifier {
1487
                            x if *x == *REQUIRE_IDENT_SPEC => {
×
1488
                                todo!()
1489
                            }
1490
                            _ => {
1491
                                return ExprKind::Quote(Box::new(Quote::new(
×
1492
                                    l.get(2).unwrap().clone(),
×
1493
                                    SyntaxObject::default(TokenType::Quote),
×
1494
                                )))
1495
                            }
1496
                        }
1497
                    }
1498

1499
                    // Then this is a contract out, and we should handle it here
1500

1501
                    ExprKind::Quote(Box::new(Quote::new(
×
1502
                        l.get(2).unwrap().clone(),
×
1503
                        SyntaxObject::default(TokenType::Quote),
×
1504
                    )))
1505
                    // ExprKind::Quote(Box::new(Quote::new(
1506
                    //     x,
1507
                    //     SyntaxObject::default(TokenType::Quote),
1508
                    // )))
1509
                } else {
1510
                    panic!("TODO this shouldn't be possible")
×
1511
                }
1512
            }),
1513
            right,
1514
        ));
1515

1516
        let module_define = ExprKind::Define(Box::new(Define::new(
1517
            ExprKind::atom(CompactString::new(MODULE_PREFIX) + &prefix),
1518
            ExprKind::List(List::new(hash_body)),
1519
            SyntaxObject::default(TokenType::Quote),
1520
        )));
1521

1522
        // let mut offset = None;
1523

1524
        // Find offset of first non builtin require definition:
1525
        // for (idx, expr) in exprs.iter().enumerate() {
1526
        //     if let ExprKind::Define(d) = expr {
1527
        //         // if !is_a_builtin_definition(d) || !is_a_require_definition(d) {
1528
        //         if !is_a_builtin_definition(d) {
1529
        //             // println!("Found offset at: {:?}", offset);
1530

1531
        //             offset = Some(idx);
1532
        //             println!("Found offset at: {:?}", offset);
1533
        //             break;
1534
        //         }
1535
        //     }
1536
        // }
1537

1538
        exprs.push(module_define);
1539

1540
        // exprs.append(&mut provide_definitions);
1541

1542
        let mut builtin_definitions = Vec::new();
1543

1544
        exprs.retain_mut(|expr| {
317,326✔
1545
            if let ExprKind::Define(d) = expr {
626,474✔
1546
                if is_a_builtin_definition(d) {
1547
                    builtin_definitions.push(std::mem::take(expr));
281,371✔
1548
                    false
281,371✔
1549
                } else {
1550
                    true
27,777✔
1551
                }
1552
            } else {
1553
                true
8,178✔
1554
            }
1555
        });
1556

1557
        builtin_definitions.append(&mut provide_definitions);
1558
        builtin_definitions.append(&mut exprs);
1559

1560
        // provide_definitions.append(&mut builtin_definitions);
1561
        // provide_definitions.append(&mut exprs);
1562

1563
        exprs = builtin_definitions;
1564

1565
        // if let Some(offset) = offset {
1566
        // for (idx, expr) in provide_definitions.into_iter().enumerate() {
1567
        //     exprs.insert(offset + idx, expr);
1568
        // }
1569
        // } else {
1570
        // provide_definitions.append(&mut exprs);
1571
        // }
1572

1573
        // println!("MODULE DEFINITIONS----");
1574

1575
        // exprs.pretty_print();
1576

1577
        // println!("END MODULE DEFINITIONS");
1578

1579
        // exprs.pretty_print();
1580

1581
        // exprs.push(module_define);
1582

1583
        // Construct the overall definition
1584
        // TODO: Perhaps mangle these as well, especially if they have contracts associated with them
1585

1586
        // if offset.is_none() {
1587
        //     provide_definitions.append(&mut exprs);
1588
        // }
1589

1590
        // Try this out?
1591
        // let mut analysis = Analysis::from_exprs(&provide_definitions);
1592
        // let mut semantic = SemanticAnalysis::from_analysis(&mut provide_definitions, analysis);
1593

1594
        // // This is definitely broken still
1595
        // semantic.remove_unused_globals_with_prefix("mangler");
1596
        // .replace_non_shadowed_globals_with_builtins()
1597
        // .remove_unused_globals_with_prefix("mangler");
1598

1599
        Ok(ExprKind::Begin(Box::new(Begin::new(
1600
            exprs,
1601
            SyntaxObject::default(TokenType::Begin),
1602
        ))))
1603
    }
1604

1605
    // Turn the module into the AST node that represents the macro module in the stdlib
1606
    fn _to_module_ast_node(&self) -> ExprKind {
×
1607
        let mut body = vec![
×
1608
            ExprKind::Atom(Atom::new(SyntaxObject::default(TokenType::Identifier(
×
1609
                "module".into(),
×
1610
            )))),
1611
            ExprKind::Atom(Atom::new(SyntaxObject::default(TokenType::Identifier(
×
1612
                ("___".to_string() + self.name.to_str().unwrap()).into(),
×
1613
            )))),
1614
        ];
1615

1616
        // Put any provides at the top
1617
        body.append(&mut self.provides.clone());
×
1618

1619
        // Include any dependencies here
1620
        // body.append(&mut self.requires.clone());
1621

1622
        // TODO: @Matt 10/8/22
1623
        // Reconsider how to address this expansion.
1624
        // We really don't want to pollute the module space - perhaps disallow shadowed built-ins so we don't need this?
1625
        // That would probably be annoying
1626
        // let steel_base = ExprKind::List(List::new(vec![ExprKind::atom("steel/base".to_string())]));
1627

1628
        // self.ast.pretty_print();
1629

1630
        // body.push(steel_base);
1631

1632
        // Put the ast nodes inside the macro
1633
        body.append(&mut self.ast.clone());
×
1634

1635
        // TODO clean this up
1636
        let res = ExprKind::List(List::new(body));
×
1637

1638
        // if log_enabled!(target: "requires", log::Level::Debug) {
1639
        //     debug!(target: "requires", "Module ast node: {}", res.to_string());
1640
        // }
1641

1642
        res
×
1643
    }
1644
}
1645

1646
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
1647
enum MaybeRenamed {
1648
    Normal(ExprKind),
1649
    Renamed(ExprKind, ExprKind),
1650
}
1651

1652
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1653
pub struct RequireObject {
1654
    path: PathOrBuiltIn,
1655
    for_syntax: bool,
1656
    idents_to_import: Vec<MaybeRenamed>,
1657
    prefix: Option<String>,
1658
    span: Span,
1659
}
1660

1661
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1662
enum PathOrBuiltIn {
1663
    BuiltIn(Cow<'static, str>),
1664
    Path(PathBuf),
1665
}
1666

1667
impl PathOrBuiltIn {
1668
    pub fn get_path(&self) -> Cow<'_, PathBuf> {
9,048✔
1669
        match self {
9,048✔
1670
            Self::Path(p) => Cow::Borrowed(p),
267✔
1671
            Self::BuiltIn(p) => Cow::Owned(PathBuf::from(p.to_string())),
8,781✔
1672
        }
1673
    }
1674
}
1675

1676
#[derive(Default, Debug, Clone)]
1677
struct RequireObjectBuilder {
1678
    path: Option<PathOrBuiltIn>,
1679
    for_syntax: bool,
1680
    idents_to_import: Vec<MaybeRenamed>,
1681
    // Built up prefix
1682
    prefix: Option<String>,
1683
    span: Span,
1684
}
1685

1686
impl RequireObjectBuilder {
1687
    fn build(self) -> Result<RequireObject> {
3,016✔
1688
        let path = self
6,032✔
1689
            .path
3,016✔
1690
            .ok_or_else(crate::throw!(Generic => "require must have a path!"))?;
3,016✔
1691

1692
        Ok(RequireObject {
1693
            path,
1694
            for_syntax: self.for_syntax,
1695
            idents_to_import: self.idents_to_import,
1696
            prefix: self.prefix,
1697
            span: self.span,
1698
        })
1699
    }
1700
}
1701

1702
fn try_canonicalize(path: PathBuf) -> PathBuf {
146✔
1703
    std::fs::canonicalize(&path).unwrap_or_else(|_| path)
292✔
1704
}
1705

1706
/*
1707
#[derive(Default, Clone)]
1708
struct DependencyGraph {
1709
    downstream: HashMap<PathBuf, Vec<PathBuf>>,
1710
}
1711

1712
impl DependencyGraph {
1713
    // Adding edges downward.
1714
    pub fn add_edges(&mut self, parent: PathBuf, children: Vec<PathBuf>) {
1715
        self.downstream.insert(parent, children);
1716
    }
1717

1718
    pub fn remove(&mut self, parent: &PathBuf) {
1719
        self.downstream.remove(parent);
1720
    }
1721

1722
    pub fn add_edge(&mut self, parent: &PathBuf, child: PathBuf) {
1723
        if let Some(children) = self.downstream.get_mut(parent) {
1724
            children.push(child);
1725
        } else {
1726
            self.downstream.insert(parent.clone(), vec![child]);
1727
        }
1728
    }
1729

1730
    // Check everything downstream of this, to see if anything needs to be invalidated
1731
    pub fn check_downstream_changes(
1732
        &self,
1733
        root: &PathBuf,
1734
        updated_at: &crate::HashMap<PathBuf, SystemTime>,
1735
    ) -> std::io::Result<bool> {
1736
        let mut stack = vec![root];
1737

1738
        while let Some(next) = stack.pop() {
1739
            let meta = std::fs::metadata(next)?;
1740

1741
            if let Some(prev) = updated_at.get(next) {
1742
                if *prev != meta.modified()? {
1743
                    return Ok(true);
1744
                }
1745
            }
1746

1747
            if let Some(children) = self.downstream.get(next) {
1748
                for child in children {
1749
                    stack.push(child);
1750
                }
1751
            }
1752
        }
1753

1754
        Ok(false)
1755
    }
1756
}
1757
*/
1758

1759
struct ModuleBuilder<'a> {
1760
    name: PathBuf,
1761
    main: bool,
1762
    source_ast: Vec<ExprKind>,
1763
    macro_map: Arc<FxHashMap<InternedString, SteelMacro>>,
1764
    // TODO: Change the requires / requires_for_syntax to just be a require enum?
1765
    require_objects: Vec<RequireObject>,
1766

1767
    provides: Vec<ExprKind>,
1768
    provides_for_syntax: Vec<ExprKind>,
1769
    compiled_modules: &'a mut FxHashMap<PathBuf, CompiledModule>,
1770
    visited: &'a mut FxHashSet<PathBuf>,
1771
    file_metadata: &'a mut crate::HashMap<PathBuf, SystemTime>,
1772
    sources: &'a mut Sources,
1773
    kernel: &'a mut Option<Kernel>,
1774
    builtin_modules: ModuleContainer,
1775
    global_macro_map: &'a FxHashMap<InternedString, SteelMacro>,
1776
    custom_builtins: &'a HashMap<String, String>,
1777
    search_dirs: &'a [PathBuf],
1778
    module_resolvers: &'a [Arc<dyn SourceModuleResolver>],
1779
}
1780

1781
impl<'a> ModuleBuilder<'a> {
1782
    #[allow(clippy::too_many_arguments)]
1783
    #[allow(unused)]
1784
    fn main(
1,318✔
1785
        name: Option<PathBuf>,
1786
        source_ast: Vec<ExprKind>,
1787
        compiled_modules: &'a mut FxHashMap<PathBuf, CompiledModule>,
1788
        visited: &'a mut FxHashSet<PathBuf>,
1789
        file_metadata: &'a mut crate::HashMap<PathBuf, SystemTime>,
1790
        sources: &'a mut Sources,
1791
        kernel: &'a mut Option<Kernel>,
1792
        builtin_modules: ModuleContainer,
1793
        global_macro_map: &'a FxHashMap<InternedString, SteelMacro>,
1794
        custom_builtins: &'a HashMap<String, String>,
1795
        search_dirs: &'a [PathBuf],
1796
        module_resolvers: &'a [Arc<dyn SourceModuleResolver>],
1797
    ) -> Result<Self> {
1798
        // TODO don't immediately canonicalize the path unless we _know_ its coming from a path
1799
        // change the path to not always be required
1800
        // if its not required we know its not coming in
1801

1802
        #[cfg(not(target_arch = "wasm32"))]
1803
        let name = if let Some(p) = name {
2,647✔
1804
            std::fs::canonicalize(p)?
×
1805
        } else {
1806
            std::env::current_dir()?
1,307✔
1807
        };
1808

1809
        #[cfg(target_arch = "wasm32")]
1810
        let name = PathBuf::new();
×
1811

1812
        Ok(ModuleBuilder {
×
1813
            name,
×
1814
            main: true,
×
1815
            source_ast,
×
1816
            macro_map: default_prelude_macros(),
×
1817
            require_objects: Vec::new(),
×
1818
            provides: Vec::new(),
×
1819
            provides_for_syntax: Vec::new(),
×
1820
            compiled_modules,
×
1821
            visited,
×
1822
            file_metadata,
×
1823
            sources,
×
1824
            kernel,
×
1825
            builtin_modules,
×
1826
            global_macro_map,
×
1827
            custom_builtins,
×
1828
            search_dirs,
×
1829
            module_resolvers,
×
1830
        })
1831
    }
1832

1833
    fn compile(&mut self) -> Result<Vec<ExprKind>> {
2,526✔
1834
        // debug!(target: "requires", "Visiting: {:?}", self.name);
1835

1836
        // @Matt - 10/3/23
1837
        // This has a relatively fatal flaw at the moment:
1838
        /*
2,526✔
1839

1840
            (define-syntax register-plugin
2,526✔
1841
              (syntax-rules ()
2,526✔
1842
                [(register-plugin plugin-path identifiers ...)
2,526✔
1843
                 (begin
2,526✔
1844
                   (require plugin-path
2,526✔
1845
                            (only-in identifiers ...))
2,526✔
1846
                   (provide identifiers ...))]))
2,526✔
1847
        */
2,526✔
1848
        // This will fail to compile - anything that expands into a require will fail since
1849
        // require is more or less a top level primitive. What we need to do is figure
1850
        // out a way to have this be recursive - the whole compilation step should
1851
        // go again once we discover that there is another require. It shouldn't be too bad,
1852
        // but the base case has to be figured out such that we don't get infinite recursion.
1853
        // I think the condition is probably something along the following:
1854
        //
1855
        // - Did we expand anything
1856
        // - Are there require statements
1857
        //
1858
        // If we expanded anything, we then should check for require statements... maybe
1859

1860
        let mut new_exprs = Vec::new();
2,526✔
1861

1862
        // self.source_ast.pretty_print();
1863

1864
        self.collect_requires()?;
2,526✔
1865
        self.collect_provides()?;
2,526✔
1866

1867
        // if log_enabled!(log::Level::Info) {
1868
        // debug!(target: "requires", "Requires: {:#?}", self.require_objects);
1869
        // debug!(target: "requires", "Provides: {:#?}", self.provides);
1870
        // debug!(target: "requires", "Provides for-syntax: {:?}", self.provides_for_syntax);
1871
        // }
1872

1873
        if self.visited.contains(&self.name) {
2,526✔
1874
            stop!(Generic => format!("circular dependency found during module resolution with: {:?}", self.name))
×
1875
        }
1876

1877
        self.visited.insert(self.name.clone());
2,526✔
1878

1879
        if self.main {
3,844✔
1880
            let exprs = std::mem::take(&mut self.source_ast);
1,318✔
1881
            self.source_ast = exprs
1,318✔
1882
                .into_iter()
1,318✔
1883
                .filter(|x| {
16,142✔
1884
                    if let ExprKind::List(l) = x {
29,621✔
1885
                        if let Some(provide) = l.first_ident() {
2,736✔
1886
                            return *provide != *PROVIDE;
×
1887
                        }
1888
                    }
1889
                    true
12,088✔
1890
                })
1891
                .collect();
1,318✔
1892
        }
1893

1894
        self.extract_macro_defs()?;
×
1895

1896
        // TODO include built ins here
1897
        if self.require_objects.is_empty() && !self.main {
4,130✔
1898
            // We're at a leaf, put into the cache
1899
            new_exprs.push(self.compile_module()?);
495✔
1900
        } else {
1901
            // TODO come back for parsing built ins
1902
            for module in self
4,958✔
1903
                .require_objects
2,031✔
1904
                .iter()
2,031✔
1905
                .filter(|x| matches!(x.path, PathOrBuiltIn::BuiltIn(_)))
5,047✔
1906
                .map(|x| x.path.get_path())
2,927✔
1907
            {
1908
                // We've established nothing has changed with this file
1909
                // Check to see if its in the cache first
1910
                // Otherwise go ahead and compile
1911
                // If we already have compiled this module, get it from the cache
1912
                if let Some(_m) = self.compiled_modules.get(module.as_ref()) {
4,703✔
1913
                    // debug!("Getting {:?} from the module cache", module);
1914
                    // println!("Already found in the cache: {:?}", module);
1915
                    // new_exprs.push(m.to_module_ast_node());
1916
                    // No need to do anything
1917
                    continue;
×
1918
                }
1919

1920
                // TODO this is some bad crap here don't do this
1921
                let input = BUILT_INS
2,302✔
1922
                    .iter()
1923
                    .find(|x| x.0 == module.to_str().unwrap())
12,157✔
1924
                    .map(|x| Cow::Borrowed(x.1))
3,453✔
1925
                    .or_else(|| {
1,151✔
1926
                        self.custom_builtins
×
1927
                            .get(module.to_str().unwrap())
×
1928
                            .map(|x| Cow::Owned(x.to_string()))
×
1929
                    })
1930
                    .or_else(|| {
1,151✔
1931
                        self.module_resolvers
×
1932
                            .iter()
×
1933
                            .find_map(|x| x.resolve(module.to_str().unwrap()))
×
1934
                            // Insert the prelude
1935
                            .map(|mut x| {
×
1936
                                x.insert_str(0, PRELUDE_STRING);
×
1937
                                x
×
1938
                            })
1939
                            .map(Cow::Owned)
×
1940
                    })
1941
                    .ok_or_else(
1942
                        crate::throw!(Generic => "Unable to find builtin module: {:?}", module),
1,151✔
1943
                    )?;
1944

1945
                let mut new_module = ModuleBuilder::new_built_in(
1946
                    module.into_owned(),
×
1947
                    input,
×
1948
                    self.compiled_modules,
×
1949
                    self.visited,
×
1950
                    self.file_metadata,
×
1951
                    self.sources,
×
1952
                    self.kernel,
×
1953
                    self.builtin_modules.clone(),
×
1954
                    self.global_macro_map,
×
1955
                    self.custom_builtins,
×
1956
                    self.module_resolvers,
×
1957
                )?;
1958

1959
                // Walk the tree and compile any dependencies
1960
                // This will eventually put the module in the cache
1961
                let mut module_exprs = new_module.compile()?;
1,151✔
1962

1963
                new_exprs.append(&mut module_exprs);
×
1964

1965
                // Probably want to evaluate a module even if it has no provides?
1966
                if !new_module.provides.is_empty() {
×
1967
                    new_exprs.push(new_module.compile_module()?);
656✔
1968
                } else {
1969
                    // log::debug!(target: "requires", "Found no provides, skipping compilation of module: {:?}", new_module.name);
1970
                }
1971
            }
1972

1973
            // At this point, requires should be fully qualified (absolute) paths
1974

1975
            for (module, require_statement_span) in self
2,120✔
1976
                .require_objects
2,031✔
1977
                .iter()
2,031✔
1978
                .filter(|x| matches!(x.path, PathOrBuiltIn::Path(_)))
5,047✔
1979
                .map(|x| (x.path.get_path(), x.span))
89✔
1980
            {
1981
                if cfg!(target_arch = "wasm32") {
1982
                    stop!(Generic => "requiring modules is not supported for wasm");
×
1983
                }
1984

1985
                let last_modified = std::fs::metadata(module.as_ref())
178✔
1986
                    .map_err(|err| {
89✔
1987
                        let mut err = crate::SteelErr::from(err);
×
1988
                        err.prepend_message(&format!(
×
1989
                            "Attempting to load module from: {:?} ",
×
1990
                            module
×
1991
                        ));
1992
                        err.set_span(require_statement_span)
×
1993
                    })?
1994
                    .modified()?;
1995

1996
                // Check if we should compile based on the last time modified
1997
                // If we're unable to get information, we want to compile
1998
                let should_recompile =
×
1999
                    if let Some(cached_modified) = self.file_metadata.get(module.as_ref()) {
32✔
2000
                        last_modified != *cached_modified
×
2001
                    } else {
2002
                        true
57✔
2003
                    };
2004

2005
                let mut downstream_validated = true;
×
2006

2007
                // We've established nothing has changed with this file
2008
                // Check to see if its in the cache first
2009
                // Otherwise go ahead and compile
2010
                if !should_recompile {
×
2011
                    // If we already have compiled this module, get it from the cache
2012
                    if let Some(m) = self.compiled_modules.get(module.as_ref()) {
64✔
2013
                        // debug!("Getting {:?} from the module cache", module);
2014
                        // println!("Already found in the cache: {:?}", module);
2015
                        // new_exprs.push(m.to_module_ast_node());
2016
                        // No need to do anything
2017

2018
                        // Check the dependencies all the way down.
2019
                        // if any are invalidated, re-compile the whole thing.
2020
                        let mut stack = m.downstream.clone();
×
2021

2022
                        while let Some(next) = stack.pop() {
98✔
2023
                            let meta = std::fs::metadata(&next)?;
33✔
2024

2025
                            if let Some(prev) = self.file_metadata.get(&next) {
33✔
2026
                                if *prev != meta.modified()? {
×
2027
                                    // println!(
2028
                                    //     "Detected change in {:?}, recompiling root starting from {:?}",
2029
                                    //     next,
2030
                                    //     module.as_ref()
2031
                                    // );
2032
                                    downstream_validated = false;
×
2033
                                    break;
×
2034
                                }
2035
                            }
2036

2037
                            if let Some(module) = self.compiled_modules.get(&next) {
66✔
2038
                                for child in &module.downstream {
61✔
2039
                                    stack.push(child.to_owned());
×
2040
                                }
2041
                            }
2042
                        }
2043

2044
                        if downstream_validated {
32✔
2045
                            continue;
32✔
2046
                        }
2047
                    }
2048
                }
2049

2050
                let mut new_module = ModuleBuilder::new_from_path(
2051
                    module.into_owned(),
57✔
2052
                    self.compiled_modules,
57✔
2053
                    self.visited,
57✔
2054
                    self.file_metadata,
57✔
2055
                    self.sources,
57✔
2056
                    self.kernel,
57✔
2057
                    self.builtin_modules.clone(),
57✔
2058
                    self.global_macro_map,
57✔
2059
                    self.custom_builtins,
57✔
2060
                    self.search_dirs,
57✔
2061
                    self.module_resolvers,
57✔
2062
                )?;
2063

2064
                // Walk the tree and compile any dependencies
2065
                // This will eventually put the module in the cache
2066
                let mut module_exprs = new_module.compile()?;
57✔
2067

2068
                // debug!("Inside {:?} - append {:?}", self.name, module);
2069
                // if log_enabled!(log::Level::Debug) {
2070
                //     debug!(
2071
                //         target: "modules",
2072
                //         "appending with {:?}",
2073
                //         module_exprs.iter().map(|x| x.to_string()).join(" SEP ")
2074
                //     );
2075
                // }
2076

2077
                new_exprs.append(&mut module_exprs);
×
2078

2079
                // TODO evaluate this
2080

2081
                // let mut ast = std::mem::replace(&mut new_module.source_ast, Vec::new());
2082
                // ast.append(&mut module_exprs);
2083
                // new_module.source_ast = ast;
2084

2085
                // dbg!(&new_module.name);
2086
                // dbg!(&new_module.compiled_modules.contains_key(&new_module.name));
2087

2088
                // If we need to, revisit because there are new provides
2089
                if !new_module.provides.is_empty() {
×
2090
                    new_exprs.push(new_module.compile_module()?);
55✔
2091
                // If the module hasn't yet been compiled, compile it anyway
2092
                } else if !new_module.compiled_modules.contains_key(&new_module.name) {
2✔
2093
                    // else if !new_module.compiled_modules.contains_key(&new_module.name) {
2094
                    new_exprs.push(new_module.compile_module()?);
2✔
2095
                } else if !downstream_validated {
×
2096
                    new_exprs.push(new_module.compile_module()?);
×
2097

2098
                    // log::debug!(target: "requires", "Found no provides, skipping compilation of module: {:?}", new_module.name);
2099
                    // log::debug!(target: "requires", "Module already in the cache: {}", new_module.compiled_modules.contains_key(&new_module.name));
2100
                    // log::debug!(target: "requires", "Compiled modules: {:?}", new_module.compiled_modules.keys().collect::<Vec<_>>());
2101
                }
2102

2103
                // else {
2104
                //     log::debug!(target: "requires", "Found no provides, skipping compilation of module: {:?}", new_module.name);
2105
                // }
2106
            }
2107
        }
2108

2109
        // new_exprs.pretty_print();
2110

2111
        Ok(new_exprs)
2,526✔
2112
    }
2113

2114
    // TODO: This should run again on itself, probably
2115
    fn compile_module(&mut self) -> Result<ExprKind> {
1,208✔
2116
        let mut ast = std::mem::take(&mut self.source_ast);
1,208✔
2117
        let mut provides = std::mem::take(&mut self.provides);
1,208✔
2118
        // Clone the requires... I suppose
2119
        let requires = self.require_objects.clone();
1,208✔
2120

2121
        // info!(
2122
        //     target: "requires",
2123
        //     "Into compiled module: provides for syntax: {:?}",
2124
        //     self.provides_for_syntax
2125
        // );
2126

2127
        // Attempt extracting the syntax transformers from this module
2128
        if let Some(kernel) = self.kernel.as_mut() {
2,416✔
2129
            kernel.load_syntax_transformers(&mut ast, self.name.to_str().unwrap().to_string())?
×
2130
        };
2131

2132
        for expr in ast.iter_mut() {
19,586✔
2133
            expand(expr, &self.macro_map)?;
18,378✔
2134

2135
            expand_kernel_in_env(
2136
                expr,
18,378✔
2137
                self.kernel.as_mut(),
18,378✔
2138
                self.builtin_modules.clone(),
18,378✔
2139
                // Expanding macros in the environment?
2140
                self.name.to_str().unwrap(),
18,378✔
2141
            )?;
2142

2143
            expand(expr, &self.macro_map)?;
18,378✔
2144
        }
2145

2146
        // Expand first with the macros from *this* module
2147
        // ast = ast
2148
        //     .into_iter()
2149
        //     .map(|x| {
2150
        //         expand(x, &self.macro_map)
2151
        //             .and_then(|x| {
2152
        //                 expand_kernel_in_env(
2153
        //                     x,
2154
        //                     self.kernel.as_mut(),
2155
        //                     self.builtin_modules.clone(),
2156
        //                     // Expanding macros in the environment?
2157
        //                     self.name.to_str().unwrap().to_string(),
2158
        //                 )
2159
        //             })
2160
        //             // Check here - I think it makes sense to expand the
2161
        //             // internal macros again, given now we might have defmacro
2162
        //             // style macros that get expanded into syntax-rules ones?
2163
        //             .and_then(|x| expand(x, &self.macro_map))
2164
        //     })
2165
        //     .collect::<Result<Vec<_>>>()?;
2166

2167
        for expr in provides.iter_mut() {
2,906✔
2168
            expand(expr, &self.macro_map)?;
1,698✔
2169
            // .and_then(|x| {
2170
            // expand_kernel(x, self.kernel.as_mut(), self.builtin_modules.clone())
2171
            expand_kernel_in_env(
2172
                expr,
1,698✔
2173
                self.kernel.as_mut(),
1,698✔
2174
                self.builtin_modules.clone(),
1,698✔
2175
                // Expanding macros in the environment?
2176
                &self.name.to_str().unwrap(),
1,698✔
2177
            )?;
2178
            // })
2179
        }
2180

2181
        // Expand provides for any macros that exist within there
2182
        // provides = provides
2183
        //     .into_iter()
2184
        //     .map(|x| {
2185
        //         expand(x, &self.macro_map).and_then(|x| {
2186
        //             // expand_kernel(x, self.kernel.as_mut(), self.builtin_modules.clone())
2187
        //             expand_kernel_in_env(
2188
        //                 x,
2189
        //                 self.kernel.as_mut(),
2190
        //                 self.builtin_modules.clone(),
2191
        //                 // Expanding macros in the environment?
2192
        //                 self.name.to_str().unwrap().to_string(),
2193
        //             )
2194
        //         })
2195
        //     })
2196
        //     .collect::<Result<Vec<_>>>()?;
2197

2198
        let mut mangled_asts = Vec::with_capacity(ast.len() + 16);
1,208✔
2199
        let mut downstream = Vec::new();
1,208✔
2200

2201
        // Look for the modules in the requires for syntax
2202
        for require_object in self.require_objects.iter()
1,652✔
2203
        // .filter(|x| x.for_syntax)
2204
        {
2205
            let require_for_syntax = require_object.path.get_path();
1,652✔
2206

2207
            if let PathOrBuiltIn::Path(_) = &require_object.path {
1,695✔
2208
                downstream.push(require_for_syntax.clone().into_owned());
43✔
2209
            }
2210

2211
            let (module, in_scope_macros, mut name_mangler) = ModuleManager::find_in_scope_macros(
2212
                self.compiled_modules,
1,652✔
2213
                require_for_syntax.as_ref(),
1,652✔
2214
                &require_object,
1,652✔
2215
                &mut mangled_asts,
1,652✔
2216
            );
2217

2218
            // let kernel_macros_in_scope: HashSet<_> =
2219
            //     module.provides_for_syntax.iter().cloned().collect();
2220

2221
            // let module_name = Cow::from(module.name.to_str().unwrap().to_string());
2222

2223
            for expr in ast.iter_mut() {
18,339✔
2224
                // First expand the in scope macros
2225
                // These are macros
2226
                let mut expander = Expander::new(&in_scope_macros);
16,687✔
2227
                expander.expand(expr)?;
16,687✔
2228
                let changed = false;
16,687✔
2229

2230
                // dbg!(expander.changed);
2231

2232
                // (first_round_expanded, changed) = expand_kernel_in_env_with_allowed(
2233
                //     first_round_expanded,
2234
                //     self.kernel.as_mut(),
2235
                //     // We don't need to expand those here
2236
                //     ModuleContainer::default(),
2237
                //     module.name.to_str().unwrap().to_string(),
2238
                //     &kernel_macros_in_scope,
2239
                // )?;
2240

2241
                // If the kernel expander expanded into something - go ahead
2242
                // and expand all of the macros in this
2243
                // if changed || expander.changed {
2244
                // Expand here?
2245
                // first_round_expanded = expand(first_round_expanded, &module.macro_map)?;
2246

2247
                // Probably don't need this
2248
                // (first_round_expanded, changed) = expand_kernel_in_env_with_change(
2249
                //     first_round_expanded,
2250
                //     self.kernel.as_mut(),
2251
                //     ModuleContainer::default(),
2252
                //     module.name.to_str().unwrap().to_string(),
2253
                // )?;
2254

2255
                // name_mangler.visit(&mut first_round_expanded);
2256
                // }
2257

2258
                if expander.changed || changed {
31,912✔
2259
                    // let source_id = self.sources.get_source_id(&module.name).unwrap();
2260

2261
                    let mut fully_expanded = expr;
1,462✔
2262
                    expand(&mut fully_expanded, &module.macro_map)?;
1,462✔
2263

2264
                    // Expanding the kernel with only these macros...
2265
                    let changed = expand_kernel_in_env_with_change(
2266
                        &mut fully_expanded,
1,462✔
2267
                        self.kernel.as_mut(),
1,462✔
2268
                        // We don't need to expand those here
2269
                        ModuleContainer::default(),
1,462✔
2270
                        &module.name.to_str().unwrap(),
1,462✔
2271
                        // &kernel_macros_in_scope,
2272
                    )?;
2273

2274
                    if changed {
18✔
2275
                        name_mangler.visit(&mut fully_expanded);
18✔
2276
                    }
2277

2278
                    // lifted_kernel_environments.insert(
2279
                    //     module_name.clone(),
2280
                    //     KernelDefMacroSpec {
2281
                    //         env: module_name,
2282
                    //         exported: None,
2283
                    //         name_mangler: name_mangler.clone(),
2284
                    //     },
2285
                    // );
2286

2287
                    // Ok(fully_expanded)
2288
                }
2289
                // else {
2290
                //     Ok(first_round_expanded)
2291
                // }
2292
            }
2293

2294
            // ast = ast
2295
            //     .into_iter()
2296
            //     .map(|x| {
2297
            //         // First expand the in scope macros
2298
            //         // These are macros
2299
            //         let mut expander = Expander::new(&in_scope_macros);
2300
            //         let mut first_round_expanded = expander.expand(x)?;
2301
            //         let mut changed = false;
2302

2303
            //         // dbg!(expander.changed);
2304

2305
            //         // (first_round_expanded, changed) = expand_kernel_in_env_with_allowed(
2306
            //         //     first_round_expanded,
2307
            //         //     self.kernel.as_mut(),
2308
            //         //     // We don't need to expand those here
2309
            //         //     ModuleContainer::default(),
2310
            //         //     module.name.to_str().unwrap().to_string(),
2311
            //         //     &kernel_macros_in_scope,
2312
            //         // )?;
2313

2314
            //         // If the kernel expander expanded into something - go ahead
2315
            //         // and expand all of the macros in this
2316
            //         // if changed || expander.changed {
2317
            //         // Expand here?
2318
            //         // first_round_expanded = expand(first_round_expanded, &module.macro_map)?;
2319

2320
            //         // Probably don't need this
2321
            //         // (first_round_expanded, changed) = expand_kernel_in_env_with_change(
2322
            //         //     first_round_expanded,
2323
            //         //     self.kernel.as_mut(),
2324
            //         //     ModuleContainer::default(),
2325
            //         //     module.name.to_str().unwrap().to_string(),
2326
            //         // )?;
2327

2328
            //         // name_mangler.visit(&mut first_round_expanded);
2329
            //         // }
2330

2331
            //         if expander.changed || changed {
2332
            //             // let source_id = self.sources.get_source_id(&module.name).unwrap();
2333

2334
            //             let mut fully_expanded = first_round_expanded;
2335
            //             expand(&mut fully_expanded, &module.macro_map)?;
2336

2337
            //             let module_name = module.name.to_str().unwrap().to_string();
2338

2339
            //             // Expanding the kernel with only these macros...
2340
            //             let changed = expand_kernel_in_env_with_change(
2341
            //                 &mut fully_expanded,
2342
            //                 self.kernel.as_mut(),
2343
            //                 // We don't need to expand those here
2344
            //                 ModuleContainer::default(),
2345
            //                 module_name.clone(),
2346
            //                 // &kernel_macros_in_scope,
2347
            //             )?;
2348

2349
            //             if changed {
2350
            //                 name_mangler.visit(&mut fully_expanded);
2351
            //             }
2352

2353
            //             // lifted_kernel_environments.insert(
2354
            //             //     module_name.clone(),
2355
            //             //     KernelDefMacroSpec {
2356
            //             //         env: module_name,
2357
            //             //         exported: None,
2358
            //             //         name_mangler: name_mangler.clone(),
2359
            //             //     },
2360
            //             // );
2361

2362
            //             Ok(fully_expanded)
2363
            //         } else {
2364
            //             Ok(first_round_expanded)
2365
            //         }
2366
            //     })
2367
            //     .collect::<Result<_>>()?;
2368

2369
            for expr in provides.iter_mut() {
3,287✔
2370
                // First expand the in scope macros
2371
                // These are macros
2372
                let mut expander = Expander::new(&in_scope_macros);
×
2373
                expander.expand(expr)?;
×
2374

2375
                if expander.changed {
1,635✔
2376
                    expand(expr, &module.macro_map)?
165✔
2377
                }
2378
                // else {
2379
                //     Ok(first_round_expanded)
2380
                // }
2381
            }
2382

2383
            // provides = provides
2384
            //     .into_iter()
2385
            //     .map(|x| {
2386
            //         // First expand the in scope macros
2387
            //         // These are macros
2388
            //         let mut expander = Expander::new(&in_scope_macros);
2389
            //         let first_round_expanded = expander.expand(x)?;
2390

2391
            //         expander.expand(&mut expr);
2392

2393
            //         if expander.changed {
2394
            //             expand(first_round_expanded, &module.macro_map)
2395
            //         } else {
2396
            //             Ok(first_round_expanded)
2397
            //         }
2398
            //     })
2399
            //     .collect::<Result<_>>()?;
2400
        }
2401

2402
        // let requires_before = self.require_objects.len();
2403

2404
        // self.collect_requires()?;
2405

2406
        // if self.require_objects.len() > requires_before {
2407
        //     println!("EXPANDED INTO A REQUIRE");
2408
        // }
2409

2410
        // TODO: Check HERE for whether there are more requires than were previously found.
2411
        // If so, we should go back and compile the module again
2412

2413
        for expr in &mut ast {
37,964✔
2414
            lower_entire_ast(expr)?;
18,378✔
2415
            FlattenBegin::flatten(expr);
18,378✔
2416
        }
2417

2418
        // TODO: @Matt - fix this hack
2419
        {
2420
            self.source_ast = ast;
1,208✔
2421
            self.provides = provides;
1,208✔
2422

2423
            // println!("Collecting provides again:");
2424
            // println!("{}", self.source_ast);
2425
            // self.source_ast.pretty_print();
2426
            self.collect_provides()?;
1,208✔
2427

2428
            provides = std::mem::take(&mut self.provides);
1,208✔
2429
            ast = std::mem::take(&mut self.source_ast);
1,208✔
2430
        }
2431

2432
        // Put the mangled asts at the top
2433
        // then include the ast there
2434
        // mangled_asts.append(&mut ast);
2435

2436
        for expr in mangled_asts.iter_mut() {
×
2437
            lower_entire_ast(expr)?;
×
2438

2439
            FlattenBegin::flatten(expr);
×
2440
        }
2441

2442
        mangled_asts.append(&mut ast);
1,208✔
2443

2444
        // mangled_asts = mangled_asts
2445
        //     .into_iter()
2446
        //     .map(lower_entire_ast)
2447
        //     // We want this to at least be flattened for querying later
2448
        //     .map(|x| {
2449
        //         x.map(|mut o| {
2450
        //             FlattenBegin::flatten(&mut o);
2451
        //             o
2452
        //         })
2453
        //     })
2454
        //     .collect::<std::result::Result<_, ParseError>>()?;
2455

2456
        // Take ast, expand with self modules, then expand with each of the require for-syntaxes
2457
        // Then mangle the require-for-syntax, include the mangled directly in the ast
2458

2459
        // @Matt: 11/15/2024
2460
        // Try collecting the provides again?
2461

2462
        // TODO: Come back here - we're going to need to figure out the require objects
2463
        let mut module = CompiledModule::new(
2464
            self.name.clone(),
1,208✔
2465
            provides,
1,208✔
2466
            requires,
1,208✔
2467
            self.provides_for_syntax
1,208✔
2468
                .iter()
1,208✔
2469
                .map(|x| *x.atom_identifier().unwrap())
1,893✔
2470
                .collect(),
×
2471
            // std::mem::take(&mut self.macro_map),
2472
            self.macro_map.clone(),
×
2473
            mangled_asts,
×
2474
            downstream,
×
2475
        );
2476

2477
        module.set_emitted(true);
×
2478

2479
        // println!(
2480
        //     "-------------- Emitting module: {:?} ----------------------",
2481
        //     self.name
2482
        // );
2483

2484
        let result = module.to_top_level_module(self.compiled_modules, self.global_macro_map)?;
1,208✔
2485

2486
        // println!("{}", result.to_pretty(60));
2487

2488
        // println!("------------------ Finish ----------------------------------");
2489

2490
        // let mut analysis = Analysis::from_exprs(&[result]);
2491

2492
        // let mut semantic = SemanticAnalysis::from_analysis(&mut result, analysis);
2493

2494
        // // This is definitely broken still
2495
        // semantic
2496
        //     .remove_unused_globals_with_prefix("mangler");
2497

2498
        // log::debug!(target: "requires", "Adding compiled module: {:?}", self.name);
2499
        // println!("Adding compiled module: {:?}", self.name);
2500
        // for (key, smacro) in module.macro_map.iter() {
2501
        //     println!("{}", key.resolve());
2502
        //     for expr in smacro.exprs() {
2503
        //         println!("{}", expr);
2504
        //     }
2505
        // }
2506

2507
        self.compiled_modules.insert(self.name.clone(), module);
×
2508

2509
        Ok(result)
×
2510
    }
2511

2512
    fn extract_macro_defs(&mut self) -> Result<()> {
2,526✔
2513
        // Probably don't have more than 128 macros in a module, but who knows?
2514
        // let mut macro_indices = SmallVec::<[usize; 128]>::new();
2515

2516
        // let exprs = std::mem::take(&mut self.source_ast);
2517

2518
        let mut error = None;
2,526✔
2519

2520
        self.source_ast.retain_mut(|expr| {
37,890✔
2521
            if let ExprKind::Macro(_) = expr {
35,364✔
2522
                // Replace with dummy begin value so we don't have to copy
2523
                // everything other for every macro definition
2524
                let mut taken_expr = ExprKind::Begin(Box::new(Begin::new(
1,506✔
2525
                    Vec::new(),
1,506✔
2526
                    SyntaxObject::default(TokenType::Begin),
1,506✔
2527
                )));
2528

2529
                std::mem::swap(expr, &mut taken_expr);
1,506✔
2530

2531
                if let ExprKind::Macro(m) = taken_expr {
1,506✔
2532
                    match SteelMacro::parse_from_ast_macro(m) {
1,506✔
2533
                        Ok(generated_macro) => {
1,506✔
2534
                            let name = generated_macro.name();
1,506✔
2535

2536
                            Arc::make_mut(&mut self.macro_map).insert(*name, generated_macro);
1,506✔
2537
                        }
2538
                        Err(e) => {
×
2539
                            if error.is_none() {
×
2540
                                error = Some(e);
×
2541
                            }
2542
                            // error = Some(e);
2543
                            return false;
×
2544
                        }
2545
                    }
2546
                } else {
2547
                    unreachable!();
×
2548
                }
2549

2550
                return false;
×
2551
            }
2552

2553
            true
33,858✔
2554
        });
2555

2556
        if let Some(e) = error {
2,526✔
2557
            return Err(e);
×
2558
        }
2559

2560
        Ok(())
2,526✔
2561
    }
2562

2563
    // Takes out the (for-syntax) forms from the provides
2564
    fn filter_out_for_syntax_provides(&mut self, exprs: Vec<ExprKind>) -> Result<Vec<ExprKind>> {
2,057✔
2565
        let mut normal_provides = Vec::new();
2,057✔
2566

2567
        for expr in exprs {
38,023✔
2568
            match &expr {
17,983✔
2569
                ExprKind::Atom(_) => {
16,631✔
2570
                    normal_provides.push(expr);
16,631✔
2571
                }
2572
                ExprKind::List(l) => {
1,352✔
2573
                    if let Some(for_syntax) = l.first_ident() {
2,704✔
2574
                        match *for_syntax {
503✔
2575
                            x if x == *FOR_SYNTAX => {
1,698✔
2576
                                if l.args.len() != 2 {
849✔
2577
                                    stop!(ArityMismatch => "provide expects a single identifier in the (for-syntax <ident>)"; l.location)
×
2578
                                }
2579

2580
                                // Collect the for syntax expressions
2581
                                // TODO -> remove this clone
2582
                                self.provides_for_syntax.push(l.args[1].clone());
849✔
2583
                            }
2584
                            x if x == *REQUIRE_IDENT_SPEC => {
4✔
2585
                                normal_provides.push(expr);
2✔
2586
                            }
2587
                            // x if x == *CONTRACT_OUT || x == *REQUIRE_IDENT_SPEC => {
2588
                            //     normal_provides.push(expr);
2589
                            // }
2590
                            _ => {
501✔
2591
                                normal_provides.push(expr);
501✔
2592
                                // stop!(TypeMismatch => "provide expects either an identifier, (for-syntax <ident>), or (contract/out ...)")
2593
                            }
2594
                        }
2595
                    } else {
2596
                        stop!(TypeMismatch => "provide expects either an identifier or a (for-syntax <ident>)"; l.location)
×
2597
                    }
2598
                }
2599
                _ => {
×
2600
                    stop!(TypeMismatch => "provide expects either a (for-syntax <ident>) or an ident"; opt expr.span())
×
2601
                }
2602
            }
2603
        }
2604

2605
        Ok(normal_provides)
2,057✔
2606
    }
2607

2608
    // TODO -> collect (provide (for-syntax ...))
2609
    // I think these will already be collected for the macro, however I think for syntax should be found earlier
2610
    // Otherwise the macro expansion will not be able to understand it
2611
    fn collect_provides(&mut self) -> Result<()> {
5,052✔
2612
        // let now = std::time::Instant::now();
2613

2614
        let mut non_provides = Vec::with_capacity(self.source_ast.len());
5,052✔
2615
        let exprs = std::mem::take(&mut self.source_ast);
5,052✔
2616

2617
        fn walk(
8,819✔
2618
            module_builder: &mut ModuleBuilder,
2619
            exprs: Vec<ExprKind>,
2620
            non_provides: &mut Vec<ExprKind>,
2621
        ) -> Result<()> {
2622
            for mut expr in exprs {
745,545✔
2623
                match &mut expr {
368,363✔
2624
                    ExprKind::List(l) => {
53,584✔
2625
                        if let Some(provide) = l.first_ident() {
68,746✔
2626
                            if *provide == *PROVIDE {
×
2627
                                if l.len() == 1 {
2,057✔
2628
                                    stop!(Generic => "provide expects at least one identifier to provide"; l.location);
×
2629
                                }
2630

2631
                                // Swap out the value inside the list
2632
                                let args = std::mem::take(&mut l.args);
2,057✔
2633

2634
                                let filtered =
2,057✔
2635
                                    module_builder.filter_out_for_syntax_provides(args)?;
×
2636

2637
                                l.args = filtered;
×
2638

2639
                                module_builder.provides.push(expr);
×
2640

2641
                                continue;
×
2642
                            }
2643
                        }
2644
                    }
2645
                    ExprKind::Begin(b) => {
3,767✔
2646
                        let exprs = std::mem::take(&mut b.exprs);
3,767✔
2647

2648
                        // Reserve capacity for these to be moved to the top level
2649
                        non_provides.reserve(exprs.len());
3,767✔
2650

2651
                        walk(module_builder, exprs, non_provides)?;
3,767✔
2652
                    }
2653
                    _ => {}
311,012✔
2654
                }
2655

2656
                non_provides.push(expr);
366,306✔
2657
            }
2658

2659
            Ok(())
8,819✔
2660
        }
2661

2662
        walk(self, exprs, &mut non_provides)?;
5,052✔
2663

2664
        self.source_ast = non_provides;
5,052✔
2665

2666
        // log::debug!(target: "pipeline_time", "Collecting provides time: {:?}", now.elapsed());
2667

2668
        Ok(())
5,052✔
2669
    }
2670

2671
    fn parse_require_object(
3,016✔
2672
        &mut self,
2673
        home: &Option<PathBuf>,
2674
        r: &crate::parser::ast::Require,
2675
        atom: &ExprKind,
2676
    ) -> Result<RequireObject> {
2677
        let mut object = RequireObjectBuilder::default();
3,016✔
2678

2679
        // Set the span so we can pass through an error if the
2680
        // module is not found
2681
        object.span = r.location.span;
3,016✔
2682

2683
        self.parse_require_object_inner(home, r, atom, &mut object)
3,016✔
2684
            .and_then(|_| object.build())
9,048✔
2685
    }
2686

2687
    // TODO: Recursively crunch the requires to gather up the necessary information
2688
    fn parse_require_object_inner(
3,021✔
2689
        &mut self,
2690
        home: &Option<PathBuf>,
2691
        r: &crate::parser::ast::Require,
2692
        atom: &ExprKind,
2693
        require_object: &mut RequireObjectBuilder,
2694
    ) -> Result<()> {
2695
        match atom {
2,236✔
2696
            ExprKind::Atom(Atom {
×
2697
                syn:
×
2698
                    SyntaxObject {
×
2699
                        ty: TokenType::StringLiteral(s),
2,236✔
2700
                        span,
2,236✔
2701
                        ..
2,236✔
2702
                    },
2703
            }) => {
2,236✔
2704
                if require_object.path.is_some() {
2,236✔
2705
                    stop!(Generic => "require object only expects one path!")
×
2706
                }
2707

2708
                // Try this?
2709
                if let Some(lib) = BUILT_INS.into_iter().cloned().find(|x| x.0 == s.as_str()) {
22,429✔
2710
                    // self.built_ins.push(PathBuf::from(lib.0));
2711

2712
                    require_object.path = Some(PathOrBuiltIn::BuiltIn(lib.0.into()));
×
2713

2714
                    return Ok(());
×
2715
                    // continue;
2716
                }
2717

2718
                if self.custom_builtins.contains_key(s.as_str()) {
79✔
2719
                    require_object.path =
×
2720
                        Some(PathOrBuiltIn::BuiltIn(s.clone().to_string().into()));
×
2721

2722
                    return Ok(());
×
2723
                }
2724

2725
                if self
79✔
2726
                    .module_resolvers
79✔
2727
                    .iter()
2728
                    .find(|x| x.exists(s.as_str()))
79✔
2729
                    .is_some()
2730
                {
2731
                    require_object.path =
×
2732
                        Some(PathOrBuiltIn::BuiltIn(s.clone().to_string().into()));
×
2733

2734
                    return Ok(());
×
2735
                }
2736

2737
                if cfg!(target_arch = "wasm32") {
2738
                    stop!(Generic => "requiring modules is not supported for wasm");
×
2739
                }
2740

2741
                let mut current = self.name.clone();
×
2742
                if current.is_file() {
47✔
2743
                    current.pop();
47✔
2744
                }
2745
                current.push(PathBuf::from(s.as_str()));
×
2746

2747
                // // If the path exists on its own, we can continue
2748
                // // But theres the case where we're searching for a module on the STEEL_HOME
2749
                if !current.exists() {
×
2750
                    if let Some(mut home) = home.clone() {
42✔
2751
                        home.push(PathBuf::from(s.as_str()));
×
2752
                        current = home;
×
2753

2754
                        log::info!("Searching STEEL_HOME for {:?}", current);
×
2755

2756
                        if !current.exists() {
×
2757
                            for dir in self.search_dirs {
×
2758
                                let mut dir = dir.clone();
×
2759
                                dir.push(s.as_str());
×
2760

2761
                                if dir.exists() {
×
2762
                                    current = dir;
×
2763
                                    break;
×
2764
                                }
2765
                            }
2766
                        }
2767
                    } else {
2768
                        // TODO: Check if this module exists in STEEL_HOME first. If it does, we'll take that as our candidate
2769
                        // and then continue on to the final module resolution part.
2770
                        //
2771
                        // If it doesn't exist, we should iterate through the search directories and attempt to find
2772
                        // a matching path there.
2773

2774
                        for dir in self.search_dirs {
×
2775
                            let mut dir = dir.clone();
×
2776
                            dir.push(s.as_str());
×
2777

2778
                            if dir.exists() {
×
2779
                                current = dir;
×
2780
                                break;
×
2781
                            }
2782
                        }
2783

2784
                        stop!(Generic => format!("Module not found: {:?} with STEEL_HOME: {:?}", current, home); *span)
×
2785
                    }
2786
                }
2787

2788
                // Get the absolute path and store that
2789
                // self.requires.push(current)
2790

2791
                let current = try_canonicalize(current);
79✔
2792

2793
                require_object.path = Some(PathOrBuiltIn::Path(current));
79✔
2794
            }
2795

2796
            // TODO: Requires with qualifiers, that aren't just for-syntax
2797
            // Perhaps like:
2798
            // (with-prefix <xyz>)
2799
            ExprKind::List(l) => {
785✔
2800
                match l.first_ident() {
785✔
2801
                    Some(x) if *x == *ONLY_IN => {
793✔
2802
                        if l.args.len() < 2 {
4✔
2803
                            stop!(BadSyntax => "only-in expects a require-spec and optionally a list of ids to bind (maybe renamed)"; l.location);
×
2804
                        }
2805

2806
                        self.parse_require_object_inner(home, r, &l.args[1], require_object)?;
4✔
2807

2808
                        for remaining in &l.args[2..] {
17✔
2809
                            match remaining {
13✔
2810
                                ExprKind::Atom(_) => {
11✔
2811
                                    require_object
11✔
2812
                                        .idents_to_import
11✔
2813
                                        .push(MaybeRenamed::Normal(remaining.clone()));
11✔
2814
                                }
2815
                                ExprKind::List(l) => {
2✔
2816
                                    if l.len() != 2 {
2✔
2817
                                        stop!(BadSyntax => "Expected a pair when renaming required identifiers");
×
2818
                                    }
2819

2820
                                    let from = &l.args[0];
2✔
2821
                                    let to = &l.args[1];
2✔
2822

2823
                                    if from.atom_identifier().is_none()
2✔
2824
                                        || to.atom_identifier().is_none()
2✔
2825
                                    {
2826
                                        stop!(BadSyntax => "only-in expected identifiers to rename");
×
2827
                                    }
2828

2829
                                    // (<from> <to>)
2830
                                    require_object
2✔
2831
                                        .idents_to_import
2✔
2832
                                        .push(MaybeRenamed::Renamed(from.clone(), to.clone()));
2✔
2833
                                }
2834
                                _ => {
×
2835
                                    stop!(BadSyntax => "unexpected syntax in only-in form during module requires")
×
2836
                                }
2837
                            }
2838
                        }
2839
                    }
2840

2841
                    Some(x) if *x == *PREFIX_IN => {
783✔
2842
                        if l.args.len() != 3 {
1✔
2843
                            stop!(BadSyntax => "prefix-in expects a prefix to prefix a given file or module"; l.location);
×
2844
                        }
2845

2846
                        let prefix = &l.args[1];
1✔
2847

2848
                        if let Some(prefix) = prefix.atom_identifier() {
1✔
2849
                            match &mut require_object.prefix {
×
2850
                                Some(existing_prefix) => {
×
2851
                                    // Append the new symbol to the existing prefix
2852
                                    existing_prefix.push_str(prefix.resolve());
×
2853
                                }
2854
                                None => {
1✔
2855
                                    require_object.prefix = Some(prefix.resolve().to_string());
1✔
2856
                                }
2857
                            }
2858

2859
                            self.parse_require_object_inner(home, r, &l.args[2], require_object)?;
×
2860
                        } else {
2861
                            stop!(TypeMismatch => "prefix-in expects an identifier to use for the prefix"; opt prefix.span());
×
2862
                        }
2863
                    }
2864

2865
                    Some(x) if *x == *FOR_SYNTAX => {
2,340✔
2866
                        // We're expecting something like (for-syntax "foo")
2867
                        if l.args.len() != 2 {
780✔
2868
                            stop!(BadSyntax => "for-syntax expects one string literal referring to a file or module"; l.location);
×
2869
                        }
2870

2871
                        let mod_name = &l.args[1];
780✔
2872
                        if let Some(path) = mod_name.string_literal() {
780✔
2873
                            if let Some(lib) = BUILT_INS.iter().find(|x| x.0 == path) {
7,445✔
2874
                                // self.built_ins.push(PathBuf::from(lib.0));
2875

2876
                                require_object.path = Some(PathOrBuiltIn::BuiltIn(lib.0.into()));
×
2877
                                require_object.for_syntax = true;
×
2878

2879
                                return Ok(());
×
2880
                                // continue;
2881
                            } else if self.custom_builtins.contains_key(path) {
10✔
2882
                                require_object.path =
×
2883
                                    Some(PathOrBuiltIn::BuiltIn(Cow::Owned(path.to_string())));
×
2884
                                require_object.for_syntax = true;
×
2885

2886
                                return Ok(());
×
2887
                            } else if self
10✔
2888
                                .module_resolvers
10✔
2889
                                .iter()
2890
                                .find(|x| x.exists(path))
10✔
2891
                                .is_some()
2892
                            {
2893
                                require_object.path =
×
2894
                                    Some(PathOrBuiltIn::BuiltIn(Cow::Owned(path.to_string())));
×
2895
                                require_object.for_syntax = true;
×
2896
                            } else {
2897
                                let mut current = self.name.clone();
10✔
2898
                                if current.is_file() {
20✔
2899
                                    current.pop();
10✔
2900
                                }
2901
                                current.push(PathBuf::from(path));
×
2902

2903
                                if !current.exists() {
×
2904
                                    if let Some(mut home) = home.clone() {
10✔
2905
                                        home.push(PathBuf::from(path));
×
2906
                                        current = home;
×
2907

2908
                                        if !current.exists() {
×
2909
                                            for dir in self.search_dirs {
×
2910
                                                let mut dir = dir.clone();
×
2911
                                                dir.push(path);
×
2912

2913
                                                if dir.exists() {
×
2914
                                                    current = dir;
×
2915
                                                    break;
×
2916
                                                }
2917
                                            }
2918
                                        }
2919

2920
                                        log::info!("Searching STEEL_HOME for {:?}", current);
×
2921
                                    } else {
2922
                                        for dir in self.search_dirs {
×
2923
                                            let mut dir = dir.clone();
×
2924
                                            dir.push(path);
×
2925

2926
                                            if dir.exists() {
×
2927
                                                current = dir;
×
2928
                                                break;
×
2929
                                            }
2930
                                        }
2931

2932
                                        stop!(Generic => format!("Module not found: {:?}", current); mod_name.span().unwrap())
×
2933
                                    }
2934
                                }
2935

2936
                                require_object.for_syntax = true;
10✔
2937
                                let current = try_canonicalize(current);
10✔
2938
                                require_object.path = Some(PathOrBuiltIn::Path(current));
10✔
2939
                            }
2940
                        } else {
2941
                            stop!(BadSyntax => "for-syntax expects a string literal referring to a file or module"; opt mod_name.span());
×
2942
                        }
2943
                    }
2944
                    _ => {
×
2945
                        stop!(BadSyntax => "require accepts either a string literal, a for-syntax expression or an only-in expression"; l.location)
×
2946
                    }
2947
                }
2948
            }
2949

2950
            unknown => {
×
2951
                stop!(Generic => format!("require object expected a string literal referring to a file/module, found: {}", unknown); opt atom.span())
×
2952
            }
2953
        }
2954

2955
        Ok(())
94✔
2956
    }
2957

2958
    fn collect_requires(&mut self) -> Result<()> {
2,526✔
2959
        // unimplemented!()
2960

2961
        let mut exprs_without_requires = Vec::new();
2,526✔
2962
        let exprs = std::mem::take(&mut self.source_ast);
2,526✔
2963

2964
        let home = STEEL_HOME
2,526✔
2965
            .clone()
2966
            .map(|x| {
5,052✔
2967
                // TODO: Fix this - try to hack in a root drive
2968
                // for windows if a unix path is provided
2969
                if cfg!(target_os = "windows") {
2,526✔
2970
                    let mut result = x.trim_start_matches("/").to_string();
×
2971

2972
                    let mut iter = result.chars();
×
2973
                    iter.next();
×
2974
                    if matches!(iter.next(), Some(':')) {
×
2975
                        return PathBuf::from(result);
×
2976
                    }
2977

2978
                    result.insert(1, ':');
×
2979
                    return PathBuf::from(result);
×
2980
                }
2981

2982
                PathBuf::from(x)
2,526✔
2983
            })
2984
            .map(|mut x| {
5,052✔
2985
                x.push("cogs");
2,526✔
2986

2987
                x
2,526✔
2988
            });
2989

2990
        fn walk(
2,526✔
2991
            module_builder: &mut ModuleBuilder,
2992
            home: &Option<PathBuf>,
2993
            exprs_without_requires: &mut Vec<ExprKind>,
2994
            exprs: Vec<ExprKind>,
2995
        ) -> Result<()> {
2996
            for expr in exprs {
82,710✔
2997
                match expr {
40,092✔
2998
                    // Include require/for-syntax here
2999
                    // This way we have some understanding of what dependencies a file has
3000
                    ExprKind::Require(r) => {
2,675✔
3001
                        for atom in &r.modules {
8,707✔
3002
                            // TODO: Consider making this not a reference for r
3003
                            let require_object =
3,016✔
3004
                                module_builder.parse_require_object(&home, &r, atom)?;
3,016✔
3005

3006
                            module_builder.require_objects.push(require_object);
×
3007
                        }
3008
                    }
3009
                    ExprKind::Begin(b) => {
×
3010
                        walk(module_builder, home, exprs_without_requires, b.exprs)?
×
3011
                    }
3012
                    _ => exprs_without_requires.push(expr),
37,417✔
3013
                }
3014
            }
3015

3016
            Ok(())
2,526✔
3017
        }
3018

3019
        walk(self, &home, &mut exprs_without_requires, exprs)?;
2,526✔
3020

3021
        self.source_ast = exprs_without_requires;
2,526✔
3022
        Ok(())
2,526✔
3023
    }
3024

3025
    #[allow(clippy::too_many_arguments)]
3026
    fn new_built_in(
1,151✔
3027
        name: PathBuf,
3028
        input: Cow<'static, str>,
3029
        compiled_modules: &'a mut FxHashMap<PathBuf, CompiledModule>,
3030
        visited: &'a mut FxHashSet<PathBuf>,
3031
        file_metadata: &'a mut crate::HashMap<PathBuf, SystemTime>,
3032
        sources: &'a mut Sources,
3033
        kernel: &'a mut Option<Kernel>,
3034
        builtin_modules: ModuleContainer,
3035
        global_macro_map: &'a FxHashMap<InternedString, SteelMacro>,
3036
        custom_builtins: &'a HashMap<String, String>,
3037
        module_resolvers: &'a [Arc<dyn SourceModuleResolver>],
3038
    ) -> Result<Self> {
3039
        ModuleBuilder::raw(
3040
            name,
1,151✔
3041
            compiled_modules,
1,151✔
3042
            visited,
1,151✔
3043
            file_metadata,
1,151✔
3044
            sources,
1,151✔
3045
            kernel,
1,151✔
3046
            builtin_modules,
1,151✔
3047
            global_macro_map,
1,151✔
3048
            custom_builtins,
1,151✔
3049
            &[],
1,151✔
3050
            module_resolvers,
1,151✔
3051
            false,
3052
        )
3053
        .parse_builtin(input)
1,151✔
3054
    }
3055

3056
    fn new_from_path(
57✔
3057
        name: PathBuf,
3058
        compiled_modules: &'a mut FxHashMap<PathBuf, CompiledModule>,
3059
        visited: &'a mut FxHashSet<PathBuf>,
3060
        file_metadata: &'a mut crate::HashMap<PathBuf, SystemTime>,
3061
        sources: &'a mut Sources,
3062
        kernel: &'a mut Option<Kernel>,
3063
        builtin_modules: ModuleContainer,
3064
        global_macro_map: &'a FxHashMap<InternedString, SteelMacro>,
3065
        custom_builtins: &'a HashMap<String, String>,
3066
        search_dirs: &'a [PathBuf],
3067
        module_resolvers: &'a [Arc<dyn SourceModuleResolver>],
3068
    ) -> Result<Self> {
3069
        ModuleBuilder::raw(
3070
            name,
57✔
3071
            compiled_modules,
57✔
3072
            visited,
57✔
3073
            file_metadata,
57✔
3074
            sources,
57✔
3075
            kernel,
57✔
3076
            builtin_modules,
57✔
3077
            global_macro_map,
57✔
3078
            custom_builtins,
57✔
3079
            search_dirs,
57✔
3080
            module_resolvers,
57✔
3081
            true,
3082
        )
3083
        .parse_from_path()
3084
    }
3085

3086
    fn raw(
1,208✔
3087
        name: PathBuf,
3088
        compiled_modules: &'a mut FxHashMap<PathBuf, CompiledModule>,
3089
        visited: &'a mut FxHashSet<PathBuf>,
3090
        file_metadata: &'a mut crate::HashMap<PathBuf, SystemTime>,
3091
        sources: &'a mut Sources,
3092
        kernel: &'a mut Option<Kernel>,
3093
        builtin_modules: ModuleContainer,
3094
        global_macro_map: &'a FxHashMap<InternedString, SteelMacro>,
3095
        custom_builtins: &'a HashMap<String, String>,
3096
        search_dirs: &'a [PathBuf],
3097
        module_resolvers: &'a [Arc<dyn SourceModuleResolver>],
3098
        canonicalize: bool,
3099
    ) -> Self {
3100
        // println!("New module found: {:?}", name);
3101

3102
        let name = if canonicalize {
2,416✔
3103
            try_canonicalize(name)
57✔
3104
        } else {
3105
            name
1,151✔
3106
        };
3107

3108
        ModuleBuilder {
3109
            name,
3110
            main: false,
3111
            source_ast: Vec::new(),
1,208✔
3112
            // TODO: This used to be empty
3113
            macro_map: default_prelude_macros(),
1,208✔
3114
            require_objects: Vec::new(),
1,208✔
3115
            provides: Vec::new(),
1,208✔
3116
            provides_for_syntax: Vec::new(),
1,208✔
3117
            compiled_modules,
3118
            visited,
3119
            file_metadata,
3120
            sources,
3121
            kernel,
3122
            builtin_modules,
3123
            global_macro_map,
3124
            custom_builtins,
3125
            search_dirs,
3126
            module_resolvers,
3127
        }
3128
    }
3129

3130
    fn parse_builtin(mut self, input: Cow<'static, str>) -> Result<Self> {
1,151✔
3131
        #[cfg(feature = "profiling")]
3132
        let now = std::time::Instant::now();
1,151✔
3133

3134
        let id = self
1,151✔
3135
            .sources
1,151✔
3136
            .add_source(input.clone(), Some(self.name.clone()));
1,151✔
3137

3138
        let parsed = Parser::new_from_source(&input, self.name.clone(), Some(id))
2,302✔
3139
            .without_lowering()
3140
            .map(|x| x.and_then(lower_macro_and_require_definitions))
24,489✔
3141
            .collect::<std::result::Result<Vec<_>, ParseError>>()?;
3142

3143
        self.source_ast = parsed;
×
3144

3145
        #[cfg(feature = "profiling")]
3146
        log::debug!(target: "pipeline_time", "Parsing: {:?} - {:?}", self.name, now.elapsed());
×
3147

3148
        // self.source_ast.pretty_print();
3149

3150
        Ok(self)
×
3151
    }
3152

3153
    fn parse_from_path(mut self) -> Result<Self> {
57✔
3154
        log::info!("Opening: {:?}", self.name);
57✔
3155
        let mut exprs = String::new();
57✔
3156

3157
        // If we were unable to resolve it via any of the built in module resolvers,
3158
        // then we check the file system.
3159
        let mut file = std::fs::File::open(&self.name).map_err(|err| {
114✔
3160
            let mut err = crate::SteelErr::from(err);
×
3161
            err.prepend_message(&format!("Attempting to load module from: {:?}", self.name));
×
3162
            err
×
3163
        })?;
3164
        self.file_metadata
×
3165
            .insert(self.name.clone(), file.metadata()?.modified()?);
57✔
3166

3167
        file.read_to_string(&mut exprs)?;
57✔
3168

3169
        let mut expressions = Parser::new(&PRELUDE_STRING, SourceId::none())
114✔
3170
            .without_lowering()
3171
            .map(|x| x.and_then(lower_macro_and_require_definitions))
570✔
3172
            .collect::<std::result::Result<Vec<_>, ParseError>>()?;
3173

3174
        let id = self.sources.add_source(exprs, Some(self.name.clone()));
×
3175

3176
        {
3177
            // Fetch the exprs after adding them to the sources
3178
            // We did _just_ add it, so its fine to unwrap
3179
            let guard = self.sources.sources.lock().unwrap();
×
3180

3181
            let exprs = guard.get(id).unwrap();
×
3182

3183
            let mut parsed = Parser::new_from_source(&exprs, self.name.clone(), Some(id))
57✔
3184
                .without_lowering()
3185
                .map(|x| x.and_then(lower_macro_and_require_definitions))
853✔
3186
                .collect::<std::result::Result<Vec<_>, ParseError>>()?;
3187

3188
            expressions.append(&mut parsed);
×
3189

3190
            self.source_ast = expressions;
×
3191
        }
3192

3193
        Ok(self)
×
3194
    }
3195
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc