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

mattwparas / steel / 17772690385

16 Sep 2025 04:36PM UTC coverage: 43.331% (+0.04%) from 43.289%
17772690385

Pull #519

github

web-flow
Merge 98d4fd22c into 3c10433b9
Pull Request #519: fix a bunch more clippy lints

56 of 123 new or added lines in 30 files covered. (45.53%)

8 existing lines in 3 files now uncovered.

12416 of 28654 relevant lines covered (43.33%)

2985398.75 hits per line

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

50.39
/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::{
6
            expand_kernel_in_env, expand_kernel_in_env_with_change, ExpanderMany, RequiredMacroMap,
7
        },
8
        interner::InternedString,
9
        kernel::Kernel,
10
        parser::{
11
            lower_entire_ast, lower_macro_and_require_definitions, ParseError, Parser, Sources,
12
            SyntaxObject,
13
        },
14
        tokens::TokenType,
15
    },
16
    steel_vm::{
17
        engine::{default_prelude_macros, ModuleContainer},
18
        transducers::interleave,
19
    },
20
};
21
use crate::{parser::expand_visitor::Expander, rvals::Result};
22

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

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

37
use crate::parser::expander::SteelMacro;
38
use crate::stop;
39

40
use std::time::SystemTime;
41

42
use crate::parser::expand_visitor::{expand, extract_macro_defs};
43

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

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

59
        let e = $e;
60

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

64
        e
65
    }};
66
}
67

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

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

83
pub(crate) const MANGLER_SEPARATOR: &str = "__%#__";
84

85
macro_rules! create_prelude {
86
    (
87
        $( $module:literal, )*
88
        $( for_syntax $module_for_syntax:literal ),*
89
    ) => {
90
        pub static PRELUDE_MODULES: &[&str] = &[
91
            $( $module, )*
92
            $( $module_for_syntax, )*
93
        ];
94

95
        pub static PRELUDE_WITHOUT_BASE: &str = concat!(
96
            $( "(require \"", $module, "\")\n", )*
97
            $( "(require (for-syntax \"", $module_for_syntax, "\"))\n", )*
98
        );
99

100
        pub static PRELUDE_STRING: &str = concat!(
101
            "(require-builtin steel/base)\n",
102
            $( "(require \"", $module, "\")\n", )*
103
            $( "(require (for-syntax \"", $module_for_syntax, "\"))\n", )*
104
        );
105
    }
106
}
107

108
// TODO: These need to be set up and interned in a stable position.
109
declare_builtins!(
110
    "steel/option" => "../scheme/modules/option.scm",
111
    "steel/result" => "../scheme/modules/result.scm",
112
    "steel/iterators" => "../scheme/modules/iterators.scm",
113
    "steel/async" => "../scheme/modules/async.scm",
114
    "steel/sync" => "../scheme/modules/sync.scm",
115
    "#%private/steel/contract" => "../scheme/modules/contracts.scm",
116
    "#%private/steel/print" => "../scheme/print.scm",
117
    "#%private/steel/ports" => "../scheme/modules/ports.scm",
118
    "#%private/steel/control" => "../scheme/modules/parameters.scm",
119
    "#%private/steel/reader" => "../scheme/modules/reader.scm",
120
    "#%private/steel/stdlib" => "../scheme/stdlib.scm",
121
    "#%private/steel/match" => "../scheme/modules/match.scm"
122
);
123

124
create_prelude!(
125
    "#%private/steel/stdlib",
126
    "#%private/steel/control",
127
    "#%private/steel/contract",
128
    "#%private/steel/print",
129
    "#%private/steel/ports",
130
    "#%private/steel/reader",
131
    "#%private/steel/match",
132
    for_syntax "#%private/steel/control",
133
    for_syntax "#%private/steel/contract"
134
);
135

136
#[cfg(not(target_family = "wasm"))]
137
pub static STEEL_SEARCH_PATHS: Lazy<Option<Vec<PathBuf>>> = Lazy::new(|| {
4✔
138
    std::env::var("STEEL_SEARCH_PATHS").ok().map(|x| {
12✔
139
        std::env::split_paths(x.as_str())
×
140
            .map(PathBuf::from)
×
141
            .collect::<Vec<_>>()
×
142
    })
143
});
144

145
pub fn steel_search_dirs() -> Vec<PathBuf> {
8✔
146
    #[cfg(not(target_family = "wasm"))]
147
    return STEEL_SEARCH_PATHS.clone().unwrap_or_default();
16✔
148

149
    #[cfg(target_family = "wasm")]
150
    return Vec::new();
151
}
152

153
#[cfg(not(target_family = "wasm"))]
154
pub static STEEL_HOME: Lazy<Option<String>> = Lazy::new(|| {
4✔
155
    std::env::var("STEEL_HOME").ok().or_else(|| {
12✔
156
        let home = env_home::env_home_dir().map(|x| x.join(".steel"));
×
157

158
        if let Some(home) = home {
×
159
            if home.exists() {
160
                return Some(home.into_os_string().into_string().unwrap());
×
161
            }
162

163
            #[cfg(target_os = "windows")]
164
            {
165
                if let Err(e) = std::fs::create_dir(&home) {
166
                    eprintln!("Unable to create steel home directory {:?}: {}", home, e)
167
                }
168

169
                return Some(home.into_os_string().into_string().unwrap());
170
            }
171
        }
172

173
        #[cfg(not(target_os = "windows"))]
174
        {
175
            let bd = xdg::BaseDirectories::new();
×
176
            let home = bd.data_home;
177

178
            home.map(|mut x: PathBuf| {
×
179
                x.push("steel");
×
180

181
                // Just go ahead and initialize the directory, even though
182
                // this is probably not the best place to do this. This almost
183
                // assuredly could be lifted out of this check since failing here
184
                // could cause some annoyance.
185
                if !x.exists() {
×
186
                    if let Err(e) = std::fs::create_dir(&x) {
×
187
                        eprintln!("Unable to create steel home directory {:?}: {}", x, e)
188
                    }
189
                }
190

191
                x.into_os_string().into_string().unwrap()
×
192
            })
193
        }
194

195
        #[cfg(target_os = "windows")]
196
        None
197
    })
198
});
199

200
#[cfg(target_family = "wasm")]
201
pub static STEEL_HOME: Lazy<Option<String>> = Lazy::new(|| None);
202

203
pub fn steel_home() -> Option<String> {
×
204
    STEEL_HOME.clone()
×
205
}
206

207
/// Manages the modules
208
/// keeps some visited state on the manager for traversal
209
/// Also keeps track of the metadata for each file in order to determine
210
/// if it needs to be recompiled
211
#[derive(Clone)]
212
pub(crate) struct ModuleManager {
213
    pub(crate) compiled_modules: crate::HashMap<PathBuf, CompiledModule>,
214
    file_metadata: crate::HashMap<PathBuf, SystemTime>,
215
    visited: FxHashSet<PathBuf>,
216
    custom_builtins: HashMap<String, String>,
217
    rollback_metadata: crate::HashMap<PathBuf, SystemTime>,
218
    // #[serde(skip_serializing, skip_deserializing)]
219
    module_resolvers: Vec<Arc<dyn SourceModuleResolver>>,
220
}
221

222
pub trait SourceModuleResolver: Send + Sync {
223
    fn resolve(&self, key: &str) -> Option<String>;
224
    fn exists(&self, key: &str) -> bool;
225
}
226

227
impl ModuleManager {
228
    pub(crate) fn new(
8✔
229
        compiled_modules: crate::HashMap<PathBuf, CompiledModule>,
230
        file_metadata: crate::HashMap<PathBuf, SystemTime>,
231
    ) -> Self {
232
        ModuleManager {
233
            compiled_modules,
234
            file_metadata,
235
            visited: FxHashSet::default(),
16✔
236
            custom_builtins: HashMap::new(),
16✔
237
            rollback_metadata: crate::HashMap::new(),
8✔
238
            module_resolvers: Vec::new(),
8✔
239
        }
240
    }
241

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

247
        self.custom_builtins.insert(module_name, text);
×
248
    }
249

250
    pub fn modules(&self) -> &crate::HashMap<PathBuf, CompiledModule> {
166,106✔
251
        &self.compiled_modules
166,106✔
252
    }
253

254
    pub fn modules_mut(&mut self) -> &mut crate::HashMap<PathBuf, CompiledModule> {
11,473✔
255
        &mut self.compiled_modules
11,473✔
256
    }
257

258
    pub(crate) fn default() -> Self {
8✔
259
        Self::new(crate::HashMap::default(), crate::HashMap::default())
24✔
260
    }
261

262
    pub(crate) fn register_module_resolver(
×
263
        &mut self,
264
        resolver: impl SourceModuleResolver + 'static,
265
    ) {
266
        self.module_resolvers.push(Arc::new(resolver));
×
267
    }
268

269
    // Add the module directly to the compiled module cache
270
    pub(crate) fn add_module(
×
271
        &mut self,
272
        path: PathBuf,
273
        global_macro_map: &mut FxHashMap<InternedString, SteelMacro>,
274
        kernel: &mut Option<Kernel>,
275
        sources: &mut Sources,
276
        builtin_modules: ModuleContainer,
277
    ) -> Result<()> {
278
        self.visited.clear();
×
279

280
        // TODO: Expand macros on the fly when visiting a module. Don't wait till the end
281
        // Macro expansion should happen as we enter a module.
282
        let mut module_builder = ModuleBuilder::new_from_path(
283
            path,
×
284
            &mut self.compiled_modules,
×
285
            &mut self.visited,
×
286
            &mut self.file_metadata,
×
287
            sources,
×
288
            kernel,
×
289
            builtin_modules,
×
290
            global_macro_map,
×
291
            &self.custom_builtins,
×
292
            &[],
×
293
            &self.module_resolvers,
×
294
        )?;
295

296
        module_builder.compile()?;
×
297

298
        Ok(())
×
299
    }
300

301
    pub(crate) fn rollback_metadata(&mut self) {
22✔
302
        self.file_metadata = self.rollback_metadata.clone();
66✔
303
    }
304

305
    // #[allow(unused)]
306
    pub(crate) fn compile_main(
11,497✔
307
        &mut self,
308
        global_macro_map: &mut FxHashMap<InternedString, SteelMacro>,
309
        kernel: &mut Option<Kernel>,
310
        sources: &mut Sources,
311
        mut exprs: Vec<ExprKind>,
312
        path: Option<PathBuf>,
313
        builtin_modules: ModuleContainer,
314
        lifted_kernel_environments: &mut HashMap<String, KernelDefMacroSpec>,
315
        lifted_macro_environments: &mut HashSet<PathBuf>,
316
        search_dirs: &[PathBuf],
317
    ) -> Result<Vec<ExprKind>> {
318
        // Wipe the visited set on entry
319
        self.visited.clear();
22,994✔
320

321
        self.rollback_metadata = self.file_metadata.clone();
34,491✔
322

323
        // TODO
324
        // This is also explicitly wrong -> we should separate the global macro map from the macros found locally in this module
325
        // For instance, (cond) is global, but (define-syntax blagh) might be local to main
326
        // if a module then defines a function (blagh) that is used inside its scope, this would expand the macro in that scope
327
        // which we do not want
328
        extract_macro_defs(&mut exprs, global_macro_map)?;
34,499✔
329

330
        let mut module_builder = ModuleBuilder::main(
331
            path,
332
            exprs,
333
            &mut self.compiled_modules,
334
            &mut self.visited,
335
            &mut self.file_metadata,
336
            sources,
337
            kernel,
338
            builtin_modules,
339
            global_macro_map,
340
            &self.custom_builtins,
341
            search_dirs,
342
            &self.module_resolvers,
343
        )?;
344

345
        let mut module_statements = module_builder.compile()?;
11,489✔
346

347
        module_builder.collect_provides()?;
×
348

349
        let mut ast = module_builder.source_ast;
11,489✔
350

351
        let mut require_defines = Vec::new();
352

353
        let mut explicit_requires = HashMap::new();
354

355
        for require_object in &module_builder.require_objects {
15,017✔
356
            let path = require_object.path.get_path();
5,292✔
357
            explicit_requires.clear();
3,528✔
358

359
            // If there _are_ explicit identifiers to import, limit what we import to what
360
            // is in the set
361
            for ident in &require_object.idents_to_import {
1,782✔
362
                match ident {
363
                    MaybeRenamed::Normal(i) => {
7✔
364
                        explicit_requires.insert(i.atom_identifier().unwrap().clone(), None);
365
                    }
366

367
                    MaybeRenamed::Renamed(from, to) => {
6✔
368
                        explicit_requires.insert(
6✔
369
                            from.atom_identifier().unwrap().clone(),
8✔
370
                            Some(to.atom_identifier().unwrap().clone()),
4✔
371
                        );
372
                    }
373
                }
374
            }
375

376
            // println!("{:?}", path);
377
            let module = if let Some(module) = module_builder.compiled_modules.get(path.as_ref()) {
8,820✔
378
                module
1,764✔
379
            } else {
380
                // log::debug!(target: "modules", "No provides found for module, skipping: {:?}", path);
381

382
                continue;
×
383
            };
384

385
            for provide_expr in &module.provides {
7,516✔
386
                // For whatever reason, the value coming into module.provides is an expression like: (provide expr...)
387
                for provide in &provide_expr.list().unwrap().args[1..] {
29,664✔
388
                    let other_module_prefix = module.prefix();
389

390
                    // TODO: Expand the contract out into something we expect
391
                    // Otherwise, this is going to blow up
392
                    match provide {
393
                        ExprKind::List(l) => {
26✔
394
                            if let Some(qualifier) = l.first_ident() {
52✔
395
                                match *qualifier {
396
                                    x if x == *REQUIRE_IDENT_SPEC => {
26✔
397
                                        // Directly expand into define/contract, but with the value just being the hash get below
398

399
                                        // (bind/c contract name 'name)
400

401
                                        let name = l.args.get(1).unwrap();
402

403
                                        if !explicit_requires.is_empty()
404
                                            && !name
9✔
405
                                                .atom_identifier()
406
                                                .map(|x| explicit_requires.contains_key(x))
27✔
407
                                                .unwrap_or_default()
408
                                        {
409
                                            continue;
6✔
410
                                        }
411

412
                                        if module
413
                                            .macro_map
414
                                            .contains_key(name.atom_identifier().unwrap())
415
                                        {
416
                                            continue;
×
417
                                        }
418

419
                                        let hash_get = expr_list![
420
                                            ExprKind::atom(*PROTO_HASH_GET),
421
                                            ExprKind::atom(
422
                                                CompactString::new(MODULE_PREFIX)
423
                                                    + &other_module_prefix
424
                                            ),
425
                                            ExprKind::Quote(Box::new(Quote::new(
426
                                                name.clone(),
427
                                                SyntaxObject::default(TokenType::Quote)
428
                                            ))),
429
                                        ];
430

431
                                        let mut owned_name = name.clone();
432

433
                                        // If we have the alias listed, we should use it
434
                                        if !explicit_requires.is_empty() {
435
                                            if let Some(alias) = explicit_requires
3✔
436
                                                .get(name.atom_identifier().unwrap())
437
                                                .copied()
438
                                                .flatten()
439
                                            {
440
                                                *owned_name.atom_identifier_mut().unwrap() =
441
                                                    alias.clone();
442
                                            }
443
                                        }
444

445
                                        if let Some(prefix) = &require_object.prefix {
3✔
446
                                            if let Some(existing) = owned_name.atom_identifier_mut()
3✔
447
                                            {
448
                                                let mut prefixed_identifier = prefix.clone();
449
                                                prefixed_identifier.push_str(existing.resolve());
450

451
                                                // Update the existing identifier to point to a new one with the prefix applied
452
                                                *existing = prefixed_identifier.into();
453
                                            }
454
                                        }
455

456
                                        let define = ExprKind::Define(Box::new(Define::new(
457
                                            owned_name,
458
                                            hash_get,
459
                                            SyntaxObject::default(TokenType::Define),
460
                                        )));
461

462
                                        require_defines.push(define);
463
                                    }
464
                                    _ => {
465
                                        stop!(TypeMismatch => format!("provide expects either an identifier, (for-syntax <ident>), or (contract/out ...), found: {}", provide))
×
466
                                    }
467
                                }
468
                            } else {
469
                                stop!(TypeMismatch => "provide expects either an identifier or a (for-syntax <ident>)")
×
470
                            }
471
                        }
472
                        ExprKind::Atom(_) => {
473
                            if !explicit_requires.is_empty()
29,638✔
474
                                && !provide
27✔
475
                                    .atom_identifier()
476
                                    .map(|x| explicit_requires.contains_key(x))
81✔
477
                                    .unwrap_or_default()
478
                            {
479
                                continue;
21✔
480
                            }
481

482
                            if module
483
                                .macro_map
484
                                .contains_key(provide.atom_identifier().unwrap())
485
                            {
486
                                continue;
577✔
487
                            }
488

489
                            let hash_get = expr_list![
490
                                ExprKind::atom(*PROTO_HASH_GET),
491
                                ExprKind::atom(
492
                                    CompactString::new(MODULE_PREFIX) + &other_module_prefix
493
                                ),
494
                                ExprKind::Quote(Box::new(Quote::new(
495
                                    provide.clone(),
496
                                    SyntaxObject::default(TokenType::Quote)
497
                                ))),
498
                            ];
499

500
                            let mut owned_provide = provide.clone();
501

502
                            // If we have the alias listed, we should use it
503
                            if !explicit_requires.is_empty() {
504
                                if let Some(alias) = explicit_requires
8✔
505
                                    .get(provide.atom_identifier().unwrap())
506
                                    .copied()
507
                                    .flatten()
508
                                {
509
                                    *owned_provide.atom_identifier_mut().unwrap() = alias.clone();
510
                                }
511
                            }
512

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

517
                                if let Some(existing) = owned_provide.atom_identifier_mut() {
9✔
518
                                    let mut prefixed_identifier = prefix.clone();
519
                                    prefixed_identifier.push_str(existing.resolve());
520

521
                                    // Update the existing identifier to point to a new one with the prefix applied
522
                                    *existing = prefixed_identifier.into();
523
                                }
524
                            }
525

526
                            let define = ExprKind::Define(Box::new(Define::new(
527
                                owned_provide,
528
                                hash_get,
529
                                SyntaxObject::default(TokenType::Define),
530
                            )));
531

532
                            // println!("{}", define);
533

534
                            require_defines.push(define);
535
                        }
536
                        _ => {
537
                            stop!(TypeMismatch => "provide expression needs to either be a `contract/out` form or an identifier")
×
538
                        }
539
                    }
540
                }
541
            }
542
        }
543

544
        let mut mangled_asts = Vec::with_capacity(ast.len());
11,489✔
545

546
        // TODO: Move this to the lower level as well
547
        // It seems we're only doing this expansion at the top level, but we _should_ do this at the lower level as well
548
        for require_object in module_builder.require_objects.iter()
1,764✔
549
        // .filter(|x| x.for_syntax)
550
        // .map(|x| x.path.get_path())
551
        {
552
            let require_for_syntax = require_object.path.get_path();
5,292✔
553

554
            let (module, in_scope_macros, mut name_mangler) = Self::find_in_scope_macros(
555
                &mut self.compiled_modules,
3,528✔
556
                require_for_syntax.as_ref(),
3,528✔
557
                require_object,
1,764✔
558
                &mut mangled_asts,
1,764✔
559
            );
560

561
            // let kernel_macros_in_scope: HashSet<_> =
562
            //     module.provides_for_syntax.iter().cloned().collect();
563

564
            // let defmacros_exported: HashSet<_> = module.
565

566
            // dbg!(&kernel_macros_in_scope);
567

568
            let module_name = module.name.to_str().unwrap().to_string();
5,292✔
569

570
            if let Some(kernel) = kernel.as_mut() {
3,528✔
571
                if kernel.exported_defmacros(&module_name).is_some() {
189✔
572
                    lifted_kernel_environments.insert(
567✔
573
                        module_name.clone(),
567✔
574
                        KernelDefMacroSpec {
189✔
575
                            _env: module_name.clone(),
567✔
576
                            _exported: None,
378✔
577
                            name_mangler: name_mangler.clone(),
189✔
578
                        },
579
                    );
580
                }
581
            }
582

583
            // TODO: This isn't right - only check if there are defmacro things
584
            // that we need to lift - just check the values that are in the defmacros
585
            // environment in the kernel
586
            // if !kernel_macros_in_scope.is_empty() {
587
            //     lifted_kernel_environments.insert(
588
            //         module_name.clone(),
589
            //         KernelDefMacroSpec {
590
            //             env: module_name,
591
            //             exported: None,
592
            //             name_mangler: name_mangler.clone(),
593
            //         },
594
            //     );
595
            // }
596

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

599
            for expr in ast.iter_mut() {
3,561✔
600
                // @matt 12/8/2023
601
                // The easiest thing to do here, is to go to the other module, and find
602
                // what defmacros have been exposed on the require for syntax. Once those
603
                // have been found, we run a pass with kernel expansion, limiting the
604
                // expander to only use the macros that we've exposed. After that,
605
                // we run the expansion again, using the full suite of defmacro capabilities.
606
                //
607
                // The question that remains - how to define the neat phases of what kinds
608
                // of macros can expand into what? Can defmacro -> syntax-rules -> defmacro?
609
                // This could eventually prove to be cumbersome, but it is still early
610
                // for defmacro. Plus, I need to create a syntax-case or syntax-parse
611
                // frontend before the defmacro style macros become too pervasive.
612
                //
613
                // TODO: Replicate this behavior over to builtin modules
614

615
                // First expand the in scope macros
616
                // These are macros
617
                let mut expander = Expander::new(&in_scope_macros);
618
                expander.expand(expr)?;
×
619
                let changed = false;
33✔
620

621
                // (first_round_expanded, changed) = expand_kernel_in_env_with_allowed(
622
                //     first_round_expanded,
623
                //     kernel.as_mut(),
624
                //     // We don't need to expand those here
625
                //     ModuleContainer::default(),
626
                //     module.name.to_str().unwrap().to_string(),
627
                //     &kernel_macros_in_scope,
628
                // )?;
629

630
                // If the kernel expander expanded into something - go ahead
631
                // and expand all of the macros in this
632
                // if changed || expander.changed {
633
                // Expand here?
634
                // first_round_expanded = expand(first_round_expanded, &module.macro_map)?;
635

636
                // Probably don't need this
637
                // (first_round_expanded, changed) = expand_kernel_in_env_with_change(
638
                //     first_round_expanded,
639
                //     kernel.as_mut(),
640
                //     ModuleContainer::default(),
641
                //     module.name.to_str().unwrap().to_string(),
642
                // )?;
643

644
                // This is pretty suspect, and needs to be revisited - only the output of the
645
                // macro expansion and not the whole thing needs to be mangled most likely.
646
                // Otherwise, we'll run into weird stuff?
647
                // if changed {
648
                //     name_mangler.visit(&mut first_round_expanded);
649
                // }
650
                // }
651

652
                if expander.changed || changed {
33✔
653
                    let _source_id = sources.get_source_id(&module.name).unwrap();
×
654

655
                    // let mut fully_expanded = first_round_expanded;
656

657
                    expand(
658
                        expr,
×
659
                        &module.macro_map,
×
660
                        // source_id,
661
                    )?;
662

663
                    // Expanding the kernel with only these macros...
664
                    let changed = expand_kernel_in_env_with_change(
665
                        expr,
666
                        kernel.as_mut(),
667
                        // We don't need to expand those here
668
                        ModuleContainer::default(),
669
                        &module_name,
670
                        // &kernel_macros_in_scope,
671
                    )?;
672

673
                    if changed {
×
674
                        name_mangler.visit(expr);
×
675
                    }
676

677
                    // lifted_kernel_environments.insert(
678
                    //     module_name.clone(),
679
                    //     KernelDefMacroSpec {
680
                    //         env: module_name,
681
                    //         exported: None,
682
                    //         name_mangler: name_mangler.clone(),
683
                    //     },
684
                    // );
685

686
                    // Ok(fully_expanded)
687
                }
688
                // else {
689
                //     Ok(first_round_expanded)
690
                // }
691
            }
692

693
            // ast = ast
694
            //     .into_iter()
695
            //     .map(|x| {
696
            //         // @matt 12/8/2023
697
            //         // The easiest thing to do here, is to go to the other module, and find
698
            //         // what defmacros have been exposed on the require for syntax. Once those
699
            //         // have been found, we run a pass with kernel expansion, limiting the
700
            //         // expander to only use the macros that we've exposed. After that,
701
            //         // we run the expansion again, using the full suite of defmacro capabilities.
702
            //         //
703
            //         // The question that remains - how to define the neat phases of what kinds
704
            //         // of macros can expand into what? Can defmacro -> syntax-rules -> defmacro?
705
            //         // This could eventually prove to be cumbersome, but it is still early
706
            //         // for defmacro. Plus, I need to create a syntax-case or syntax-parse
707
            //         // frontend before the defmacro style macros become too pervasive.
708
            //         //
709
            //         // TODO: Replicate this behavior over to builtin modules
710

711
            //         // First expand the in scope macros
712
            //         // These are macros
713
            //         let mut expander = Expander::new(&in_scope_macros);
714
            //         let mut first_round_expanded = expander.expand(x)?;
715
            //         let mut changed = false;
716

717
            //         // (first_round_expanded, changed) = expand_kernel_in_env_with_allowed(
718
            //         //     first_round_expanded,
719
            //         //     kernel.as_mut(),
720
            //         //     // We don't need to expand those here
721
            //         //     ModuleContainer::default(),
722
            //         //     module.name.to_str().unwrap().to_string(),
723
            //         //     &kernel_macros_in_scope,
724
            //         // )?;
725

726
            //         // If the kernel expander expanded into something - go ahead
727
            //         // and expand all of the macros in this
728
            //         // if changed || expander.changed {
729
            //         // Expand here?
730
            //         // first_round_expanded = expand(first_round_expanded, &module.macro_map)?;
731

732
            //         // Probably don't need this
733
            //         // (first_round_expanded, changed) = expand_kernel_in_env_with_change(
734
            //         //     first_round_expanded,
735
            //         //     kernel.as_mut(),
736
            //         //     ModuleContainer::default(),
737
            //         //     module.name.to_str().unwrap().to_string(),
738
            //         // )?;
739

740
            //         // This is pretty suspect, and needs to be revisited - only the output of the
741
            //         // macro expansion and not the whole thing needs to be mangled most likely.
742
            //         // Otherwise, we'll run into weird stuff?
743
            //         // if changed {
744
            //         //     name_mangler.visit(&mut first_round_expanded);
745
            //         // }
746
            //         // }
747

748
            //         if expander.changed || changed {
749
            //             let source_id = sources.get_source_id(&module.name).unwrap();
750

751
            //             let mut fully_expanded = first_round_expanded;
752

753
            //             expand(
754
            //                 &mut fully_expanded,
755
            //                 &module.macro_map,
756
            //                 // source_id,
757
            //             )?;
758

759
            //             let module_name = module.name.to_str().unwrap().to_string();
760

761
            //             // Expanding the kernel with only these macros...
762
            //             let changed = expand_kernel_in_env_with_change(
763
            //                 &mut fully_expanded,
764
            //                 kernel.as_mut(),
765
            //                 // We don't need to expand those here
766
            //                 ModuleContainer::default(),
767
            //                 module_name.clone(),
768
            //                 // &kernel_macros_in_scope,
769
            //             )?;
770

771
            //             if changed {
772
            //                 name_mangler.visit(&mut fully_expanded);
773
            //             }
774

775
            //             // lifted_kernel_environments.insert(
776
            //             //     module_name.clone(),
777
            //             //     KernelDefMacroSpec {
778
            //             //         env: module_name,
779
            //             //         exported: None,
780
            //             //         name_mangler: name_mangler.clone(),
781
            //             //     },
782
            //             // );
783

784
            //             Ok(fully_expanded)
785
            //         } else {
786
            //             Ok(first_round_expanded)
787
            //         }
788
            //     })
789
            //     .collect::<Result<_>>()?;
790

791
            // Global macro map - also need to expand with ALL macros
792
            // post expansion in the target environment, which means we can't _just_
793
            // extend the global macro map with the target in scope macros, we need to
794
            // do something like the two pass expansion
795
            global_macro_map.extend(in_scope_macros);
1,764✔
796

797
            lifted_macro_environments.insert(module.name.clone());
798
        }
799

800
        // Include the defines from the modules now imported
801
        module_statements.append(&mut require_defines);
11,489✔
802

803
        // The next two lines here expand _all_ of the source code with the top level macros
804
        // This is necessary because of the std library macros, although this should be able to be
805
        // solved with scoped imports of the standard library explicitly
806
        module_statements.append(&mut ast);
807

808
        time!("Top level macro evaluation time", {
11,483✔
809
            for expr in module_statements.iter_mut() {
56,621✔
810
                expand(expr, global_macro_map)?;
169,869✔
811
            }
812
        });
813

814
        // @Matt 7/4/23
815
        // TODO: With mangling, this could cause problems. We'll want to un-mangle quotes AFTER the macro has been expanded,
816
        // in order to preserve the existing behavior.
817
        // let result = module_statements
818
        //     .into_iter()
819
        //     .map(|x| expand(x, global_macro_map))
820
        //     .collect::<Result<_>>();
821

822
        // result
823

824
        Ok(module_statements)
825
    }
826

827
    fn find_in_scope_macros<'a>(
4,027✔
828
        compiled_modules: &'a mut crate::HashMap<PathBuf, CompiledModule>,
829
        require_for_syntax: &'a PathBuf,
830
        require_object: &'a RequireObject,
831
        mangled_asts: &'a mut Vec<ExprKind>,
832
    ) -> (
833
        &'a CompiledModule,
834
        FxHashMap<InternedString, SteelMacro>,
835
        NameMangler,
836
    ) {
837
        let module = compiled_modules
8,054✔
838
            .get_mut(require_for_syntax)
8,054✔
839
            .unwrap_or_else(|| panic!("Module missing!: {:?}", require_for_syntax));
4,027✔
840

841
        let prefix = module.prefix();
12,081✔
842

843
        // Collect globals and also, anything that is imported
844
        let mut globals = collect_globals(&module.ast);
12,081✔
845

846
        // Bring in what they provided first, but then if we can't find that, we'll have to
847
        // find all of the remaining ones by fetching all of their dependencies, and then
848
        // reborrowing
849
        globals.extend(
8,054✔
850
            module
4,027✔
851
                .require_objects
4,027✔
852
                .iter()
4,027✔
853
                .flat_map(|x| x.as_identifiers()),
10,005✔
854
        );
855

856
        let modules_to_check = module
8,054✔
857
            .require_objects
4,027✔
858
            .iter()
859
            .filter_map(|x| {
7,016✔
860
                if x.idents_to_import.is_empty() {
5,978✔
861
                    Some(x.path.get_path().into_owned())
5,978✔
862
                } else {
863
                    None
×
864
                }
865
            })
866
            .collect::<Vec<_>>();
867

868
        // drop(module);
869

870
        for importing_module in modules_to_check {
10,005✔
871
            let other_module = compiled_modules.get_mut(&importing_module).unwrap();
×
872

873
            for provide_expr in &other_module.provides {
14,267✔
874
                // TODO: Handle provide spec stuff!
875
                for provide in &provide_expr.list().unwrap().args[1..] {
47,588✔
876
                    if let Some(ident) = provide.atom_identifier() {
46,727✔
877
                        globals.insert(*ident);
×
878
                    }
879

880
                    if let Some(list) = provide.list().and_then(|x| x.second_ident()) {
2,583✔
881
                        globals.insert(*list);
×
882
                    }
883
                }
884
            }
885
        }
886

887
        let module = compiled_modules
8,054✔
888
            .get_mut(require_for_syntax)
8,054✔
889
            .unwrap_or_else(|| panic!("Module missing!: {:?}", require_for_syntax));
4,027✔
890

891
        let mut name_mangler = NameMangler::new(globals, prefix);
16,108✔
892

893
        // If the module hasn't been emitted already, then include it here
894
        if !module.emitted {
4,027✔
895
            let mut module_ast = module.ast.clone();
×
896

897
            name_mangler.mangle_vars(&mut module_ast);
×
898

899
            mangled_asts.append(&mut module_ast);
×
900
        }
901

902
        // let provided_macros = module.provides_for
903
        // let expander = Expander::new(&module.macro_map);
904
        // TODO
905
        // expand expressions one by one
906
        // if expansion with _just_ public macros from the required module doesn't do anything, stop
907
        // if it _does_ change, do another pass with all of the macros in scope
908
        // do this for each of the expressions in the file in this loop
909
        // TODO -> try not cloning this
910
        // TODO -> do this in the module expansion as well
911
        let mut in_scope_macros = module
8,054✔
912
            .provides_for_syntax
4,027✔
913
            .iter()
914
            // Chain with just the normal provides!
915
            .chain(module.provides.iter().flat_map(|x| {
19,314✔
916
                x.list().unwrap().args[1..]
14,466✔
917
                    .iter()
7,233✔
918
                    .filter_map(|x| x.atom_identifier())
140,455✔
919
            }))
920
            .filter_map(|x| {
74,226✔
921
                let smacro = Arc::make_mut(&mut module.macro_map).get_mut(x);
280,796✔
922

923
                if let Some(smacro) = smacro {
75,311✔
924
                    if !smacro.special_mangled {
×
925
                        for expr in smacro.exprs_mut() {
14,098✔
926
                            name_mangler.visit(expr);
×
927
                        }
928
                    }
929

930
                    Some((*x, smacro.clone()))
×
931
                } else {
932
                    None
65,087✔
933
                }
934

935
                // if !x.1.special_mangled {
936
                //     for expr in x.1.exprs_mut() {
937
                //         name_mangler.visit(expr);
938
                //     }
939
                // }
940

941
                // (x.0, x.1.clone())
942
            })
943
            .collect::<FxHashMap<_, _>>();
944

945
        // If the require_object specifically imports things, we should reference it
946

947
        if !require_object.idents_to_import.is_empty() {
4,027✔
948
            for maybe in &require_object.idents_to_import {
21✔
949
                match maybe {
×
950
                    MaybeRenamed::Normal(n) => {
7✔
951
                        if let Some(ident) = n.atom_identifier() {
7✔
952
                            if let Some(mut m) = module.macro_map.get(ident).cloned() {
×
953
                                for expr in m.exprs_mut() {
×
954
                                    name_mangler.visit(expr);
×
955
                                }
956

957
                                if let Some(prefix) = &require_object.prefix {
×
958
                                    in_scope_macros
×
959
                                        .insert((prefix.to_string() + ident.resolve()).into(), m);
×
960
                                } else {
961
                                    in_scope_macros.insert(*ident, m);
×
962
                                }
963
                            }
964
                        }
965
                    }
966
                    MaybeRenamed::Renamed(from, to) => {
4✔
967
                        if let Some(ident) = from.atom_identifier() {
4✔
968
                            if let Some(mut m) = module.macro_map.get(ident).cloned() {
×
969
                                for expr in m.exprs_mut() {
×
970
                                    name_mangler.visit(expr);
×
971
                                }
972
                                // TODO: Remove this unwrap
973
                                // in_scope_macros.insert(*to.atom_identifier().unwrap(), m);
974

975
                                if let Some(prefix) = &require_object.prefix {
×
976
                                    in_scope_macros.insert(
×
977
                                        (prefix.to_string()
×
978
                                            + to.atom_identifier().unwrap().resolve())
×
979
                                        .into(),
×
980
                                        m,
×
981
                                    );
982
                                } else {
983
                                    in_scope_macros.insert(*to.atom_identifier().unwrap(), m);
×
984
                                }
985
                            }
986
                        }
987
                    }
988
                }
989
            }
990
        } else {
991
            // Pull in all of the macros that the module exposes
992

993
            for provide_expr in &module.provides {
18,484✔
994
                if let Some(provide_expr) = provide_expr.list() {
7,230✔
995
                    for ident in provide_expr.args.split_first().unwrap().1 {
66,575✔
996
                        // println!("Looking for {}", ident);
997

998
                        if let Some(ident) = ident.atom_identifier() {
65,988✔
999
                            if let Some(mut m) = module.macro_map.get(ident).cloned() {
928✔
1000
                                // println!("Pulling in macro: {}", ident);
1001

1002
                                for expr in m.exprs_mut() {
1,569✔
1003
                                    name_mangler.visit(expr);
×
1004
                                }
1005

1006
                                if let Some(prefix) = &require_object.prefix {
×
1007
                                    in_scope_macros
×
1008
                                        .insert((prefix.to_string() + ident.resolve()).into(), m);
×
1009
                                } else {
1010
                                    in_scope_macros.insert(*ident, m);
2,784✔
1011
                                }
1012
                            }
1013
                        }
1014
                    }
1015
                }
1016
            }
1017
        }
1018

1019
        // Check what macros are in scope here
1020
        // println!(
1021
        //     "In scope macros: {:#?}",
1022
        //     in_scope_macros.keys().collect::<Vec<_>>()
1023
        // );
1024
        (module, in_scope_macros, name_mangler)
8,054✔
1025
    }
1026
}
1027

1028
// Pre-compile module to bytecode? Is it even possible?
1029
// Dynamically linking the module would then make it relatively
1030
// easy to just load everything up at the start.
1031
// Compiled module _should_ be possible now. Just create a target
1032
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1033
pub struct CompiledModule {
1034
    name: PathBuf,
1035
    provides: Vec<ExprKind>,
1036
    require_objects: Vec<RequireObject>,
1037
    provides_for_syntax: Vec<InternedString>,
1038
    pub(crate) macro_map: Arc<FxHashMap<InternedString, SteelMacro>>,
1039
    pub(crate) ast: Vec<ExprKind>,
1040
    emitted: bool,
1041
    cached_prefix: CompactString,
1042
    downstream: Vec<PathBuf>,
1043
}
1044

1045
pub static MANGLER_PREFIX: &str = "##mm";
1046
pub static MODULE_PREFIX: &str = "__module-";
1047
pub static MANGLED_MODULE_PREFIX: &str = "__module-##mm";
1048

1049
pub fn path_to_module_name(name: PathBuf) -> String {
×
1050
    let mut base = CompactString::new(MANGLED_MODULE_PREFIX);
×
1051

1052
    if let Some(steel_home) = STEEL_HOME.as_ref() {
×
1053
        // Intern this?
1054
        let name = name
×
1055
            .to_str()
1056
            .unwrap()
1057
            .trim_start_matches(steel_home.as_str());
×
1058

NEW
1059
        let interned = InternedString::from_str(name);
×
1060
        let id = interned.get().into_inner();
×
1061

1062
        base.push_str(&id.to_string());
×
1063
        base.push_str(MANGLER_SEPARATOR);
×
1064
    } else {
1065
        let interned = InternedString::from_str(name.to_str().unwrap());
×
1066
        let id = interned.get().into_inner();
×
1067

1068
        base.push_str(&id.to_string());
×
1069
        base.push_str(MANGLER_SEPARATOR);
×
1070
    }
1071

1072
    base.into_string()
×
1073
}
1074

1075
// TODO: @Matt 6/12/23 - This _should_ be serializable. If possible, we can try to store intermediate objects down to some file.
1076
impl CompiledModule {
1077
    pub fn new(
1,612✔
1078
        name: PathBuf,
1079
        provides: Vec<ExprKind>,
1080
        require_objects: Vec<RequireObject>,
1081
        provides_for_syntax: Vec<InternedString>,
1082
        macro_map: Arc<FxHashMap<InternedString, SteelMacro>>,
1083
        ast: Vec<ExprKind>,
1084
        downstream: Vec<PathBuf>,
1085
    ) -> Self {
1086
        let mut base = CompactString::new(MANGLER_PREFIX);
4,836✔
1087

1088
        if let Some(steel_home) = STEEL_HOME.as_ref() {
3,224✔
1089
            // Intern this?
1090
            let name = name
1091
                .to_str()
1092
                .unwrap()
1093
                .trim_start_matches(steel_home.as_str());
1094

1095
            let interned = InternedString::from_str(name);
1096
            let id = interned.get().into_inner();
1097

1098
            // base.push_str(name);
1099
            base.push_str(&id.to_string());
1100
            base.push_str(MANGLER_SEPARATOR);
1101

1102
            // println!("{}", base);
1103
            // println!("Byte length: {}", base.len());
1104
        } else {
1105
            let interned = InternedString::from_str(name.to_str().unwrap());
×
1106
            let id = interned.get().into_inner();
×
1107

1108
            // base.push_str(self.name.to_str().unwrap());
1109
            base.push_str(&id.to_string());
×
1110
            base.push_str(MANGLER_SEPARATOR);
×
1111
        }
1112

1113
        Self {
1114
            name,
1115
            provides,
1116
            require_objects,
1117
            provides_for_syntax,
1118
            macro_map,
1119
            ast,
1120
            emitted: false,
1121
            cached_prefix: base,
1122
            downstream,
1123
        }
1124
    }
1125

1126
    // TODO: Should cache this
1127
    pub fn prefix(&self) -> CompactString {
37,566✔
1128
        self.cached_prefix.clone()
75,132✔
1129
    }
1130

1131
    pub fn get_ast(&self) -> &[ExprKind] {
×
1132
        &self.ast
×
1133
    }
1134

1135
    pub fn get_provides(&self) -> &[ExprKind] {
×
1136
        &self.provides
×
1137
    }
1138

1139
    // pub fn get_requires(&self) -> &[PathBuf] {
1140
    //     &self.requires
1141
    // }
1142

1143
    pub fn set_emitted(&mut self, emitted: bool) {
1,612✔
1144
        self.emitted = emitted;
1,612✔
1145
    }
1146

1147
    fn to_top_level_module(
1,612✔
1148
        &self,
1149
        modules: &crate::HashMap<PathBuf, CompiledModule>,
1150
        global_macro_map: &FxHashMap<InternedString, SteelMacro>,
1151
    ) -> Result<ExprKind> {
1152
        let mut globals = collect_globals(&self.ast);
4,836✔
1153

1154
        let mut exprs = self.ast.clone();
4,836✔
1155

1156
        let mut provide_definitions = Vec::new();
3,224✔
1157

1158
        // TODO: Change this to not use the full path. Unfortunately that isn't portable,
1159
        // so we should use something that is more agnostic of the target location
1160
        // if we were to load.
1161
        //
1162
        // Probably a better idea would be to somehow assign a unique ID to each module;
1163
        // the path from the $STEEL_HOME would be relatively safe. That way we can just strip
1164
        // the $STEEL_HOME root away from the path if it starts with it, and then have
1165
        // that resolve to be the "name" of the module.
1166
        let prefix = self.prefix();
4,836✔
1167

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

1172
        let mut explicit_requires = HashMap::new();
3,224✔
1173

1174
        // TODO: This is the same as the top level, they should be merged
1175
        for require_object in &self.require_objects {
6,138✔
1176
            let path = require_object.path.get_path();
6,789✔
1177

1178
            explicit_requires.clear();
4,526✔
1179

1180
            for ident in &require_object.idents_to_import {
2,263✔
1181
                match ident {
1182
                    MaybeRenamed::Normal(i) => {
×
1183
                        explicit_requires.insert(i.atom_identifier().unwrap().clone(), None);
1184
                    }
1185
                    MaybeRenamed::Renamed(from, to) => {
×
1186
                        explicit_requires.insert(
×
1187
                            from.atom_identifier().unwrap().clone(),
×
1188
                            Some(to.atom_identifier().unwrap().clone()),
×
1189
                        );
1190
                    }
1191
                }
1192
            }
1193

1194
            // println!("{:?}", path);
1195
            // println!("{:?}", modules.keys().collect::<Vec<_>>());
1196
            let module = modules.get(path.as_ref()).unwrap();
11,315✔
1197

1198
            let other_module_prefix = module.prefix();
6,789✔
1199

1200
            for provide_expr in &module.provides {
10,977✔
1201
                // For whatever reason, the value coming into module.provides is an expression like: (provide expr...)
1202
                for provide in &provide_expr.list().unwrap().args[1..] {
36,947✔
1203
                    match provide {
1204
                        ExprKind::List(l) => {
570✔
1205
                            if let Some(qualifier) = l.first_ident() {
1,140✔
1206
                                if module.macro_map.contains_key(qualifier) {
1207
                                    continue;
×
1208
                                }
1209

1210
                                match *qualifier {
1211
                                    x if x == *REQUIRE_IDENT_SPEC => {
570✔
1212
                                        // Directly expand into define/contract, but with the value just being the hash get below
1213

1214
                                        // (bind/c contract name 'name)
1215

1216
                                        let name = l.args.get(1).unwrap();
1217

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

1227
                                        if module
1228
                                            .macro_map
1229
                                            .contains_key(name.atom_identifier().unwrap())
1230
                                        {
1231
                                            continue;
×
1232
                                        }
1233

1234
                                        let hash_get = expr_list![
1235
                                            ExprKind::atom(*PROTO_HASH_GET),
1236
                                            ExprKind::atom(
1237
                                                CompactString::new(MODULE_PREFIX)
1238
                                                    + &other_module_prefix
1239
                                            ),
1240
                                            ExprKind::Quote(Box::new(Quote::new(
1241
                                                name.clone(),
1242
                                                SyntaxObject::default(TokenType::Quote)
1243
                                            ))),
1244
                                        ];
1245

1246
                                        let mut owned_name = name.clone();
1247

1248
                                        // If we have the alias listed, we should use it
1249
                                        if !explicit_requires.is_empty() {
1250
                                            if let Some(alias) = explicit_requires
×
1251
                                                .get(name.atom_identifier().unwrap())
1252
                                                .copied()
1253
                                                .flatten()
1254
                                            {
1255
                                                *owned_name.atom_identifier_mut().unwrap() =
1256
                                                    alias.clone();
1257
                                            }
1258
                                        }
1259

1260
                                        if let Some(prefix) = &require_object.prefix {
×
1261
                                            if let Some(existing) = owned_name.atom_identifier_mut()
×
1262
                                            {
1263
                                                let mut prefixed_identifier = prefix.clone();
1264
                                                prefixed_identifier.push_str(existing.resolve());
1265

1266
                                                // Update the existing identifier to point to a new one with the prefix applied
1267
                                                *existing = prefixed_identifier.into();
1268
                                            }
1269
                                        }
1270

1271
                                        globals.insert(*name.atom_identifier().unwrap());
1272

1273
                                        let define = ExprKind::Define(Box::new(Define::new(
1274
                                            owned_name,
1275
                                            hash_get,
1276
                                            SyntaxObject::default(TokenType::Define),
1277
                                        )));
1278

1279
                                        provide_definitions.push(define);
1280
                                    }
1281

1282
                                    // x if x == *CONTRACT_OUT => {
1283
                                    //     // Directly expand into define/contract, but with the value just being the hash get below
1284

1285
                                    //     // (bind/c contract name 'name)
1286

1287
                                    //     let mut name = l.args.get(1).unwrap().clone();
1288
                                    //     let _contract = l.args.get(2).unwrap();
1289

1290
                                    //     if !explicit_requires.is_empty()
1291
                                    //         && !name
1292
                                    //             .atom_identifier()
1293
                                    //             .map(|x| explicit_requires.contains_key(x))
1294
                                    //             .unwrap_or_default()
1295
                                    //     {
1296
                                    //         continue;
1297
                                    //     }
1298

1299
                                    //     // If we have the alias listed, we should use it
1300
                                    //     if !explicit_requires.is_empty() {
1301
                                    //         if let Some(alias) = explicit_requires
1302
                                    //             .get(name.atom_identifier().unwrap())
1303
                                    //             .copied()
1304
                                    //             .flatten()
1305
                                    //         {
1306
                                    //             *name.atom_identifier_mut().unwrap() =
1307
                                    //                 alias.clone();
1308
                                    //         }
1309
                                    //     }
1310

1311
                                    //     if let Some(prefix) = &require_object.prefix {
1312
                                    //         if let Some(existing) = name.atom_identifier_mut() {
1313
                                    //             let mut prefixed_identifier = prefix.clone();
1314
                                    //             prefixed_identifier.push_str(existing.resolve());
1315

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

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

1326
                                    //     let hash_get = expr_list![
1327
                                    //         ExprKind::atom(*PROTO_HASH_GET),
1328
                                    //         ExprKind::atom(
1329
                                    //             "__module-".to_string() + &other_module_prefix
1330
                                    //         ),
1331
                                    //         ExprKind::Quote(Box::new(Quote::new(
1332
                                    //             name.clone(),
1333
                                    //             SyntaxObject::default(TokenType::Quote)
1334
                                    //         ))),
1335
                                    //     ];
1336

1337
                                    //     let define = ExprKind::Define(Box::new(Define::new(
1338
                                    //         ExprKind::atom(
1339
                                    //             prefix.clone()
1340
                                    //                 + name.atom_identifier().unwrap().resolve(),
1341
                                    //         ),
1342
                                    //         hash_get,
1343
                                    //         SyntaxObject::default(TokenType::Define),
1344
                                    //     )));
1345

1346
                                    //     provide_definitions.push(define);
1347
                                    // }
1348
                                    _ => {
1349
                                        stop!(TypeMismatch => format!("provide expects either an identifier, (for-syntax <ident>), or (contract/out ...) - found: {}", provide))
×
1350
                                    }
1351
                                }
1352
                            } else {
1353
                                stop!(TypeMismatch => "provide expects either an identifier or a (for-syntax <ident>)")
×
1354
                            }
1355
                        }
1356
                        ExprKind::Atom(_) => {
1357
                            if !explicit_requires.is_empty()
36,377✔
1358
                                && !provide
×
1359
                                    .atom_identifier()
1360
                                    .map(|x| explicit_requires.contains_key(x))
×
1361
                                    .unwrap_or_default()
1362
                            {
1363
                                continue;
×
1364
                            }
1365

1366
                            if module
1367
                                .macro_map
1368
                                .contains_key(provide.atom_identifier().unwrap())
1369
                            {
1370
                                continue;
351✔
1371
                            }
1372

1373
                            // Mangle with a prefix if necessary
1374
                            let mut provide = provide.clone();
1375
                            let raw_provide = provide.clone();
1376

1377
                            // If we have the alias listed, we should use it
1378
                            if !explicit_requires.is_empty() {
1379
                                if let Some(alias) = explicit_requires
×
1380
                                    .get(provide.atom_identifier().unwrap())
1381
                                    .copied()
1382
                                    .flatten()
1383
                                {
1384
                                    *provide.atom_identifier_mut().unwrap() = alias.clone();
1385
                                }
1386
                            }
1387

1388
                            if let Some(prefix) = &require_object.prefix {
×
1389
                                if let Some(existing) = provide.atom_identifier_mut() {
×
1390
                                    let mut prefixed_identifier = prefix.clone();
1391
                                    prefixed_identifier.push_str(existing.resolve());
1392

1393
                                    // Update the existing identifier to point to a new one with the prefix applied
1394
                                    *existing = prefixed_identifier.into();
1395
                                }
1396
                            }
1397

1398
                            let provide_ident = provide.atom_identifier().unwrap();
1399

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

1405
                            let define = ExprKind::Define(Box::new(Define::new(
1406
                                ExprKind::atom(prefix.clone() + provide_ident.resolve()),
1407
                                expr_list![
1408
                                    ExprKind::atom(*PROTO_HASH_GET),
1409
                                    ExprKind::atom(
1410
                                        CompactString::new(MODULE_PREFIX) + &other_module_prefix
1411
                                    ),
1412
                                    ExprKind::Quote(Box::new(Quote::new(
1413
                                        raw_provide.clone(),
1414
                                        SyntaxObject::default(TokenType::Quote)
1415
                                    )))
1416
                                ],
1417
                                SyntaxObject::default(TokenType::Define),
1418
                            )));
1419

1420
                            // if require_object.prefix.is_some() {
1421
                            //     println!("{}", define);
1422
                            // }
1423

1424
                            provide_definitions.push(define);
1425
                        }
1426
                        _ => {
1427
                            stop!(TypeMismatch => "provide expression needs to either be a `contract/out` form or an identifier")
×
1428
                        }
1429
                    }
1430
                }
1431
            }
1432
        }
1433

1434
        // Mangle all of the variables that are either:
1435
        // 1. Defined locally in this file
1436
        // 2. Required by another file
1437
        let mut name_mangler = NameMangler::new(globals, prefix.clone());
1,612✔
1438

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

1443
        name_mangler.mangle_vars(&mut exprs);
1444

1445
        // The provide definitions should also be mangled
1446
        name_mangler.mangle_vars(&mut provide_definitions);
1447

1448
        // let mut hash_builder = Vec::new();
1449

1450
        // These are gonna be the pairs
1451
        // hash_builder.push(());
1452

1453
        // Construct the series of provides as well, we'll want these to refer to the correct values
1454
        //
1455
        let mut provides: smallvec::SmallVec<[(ExprKind, ExprKind); 24]> = self
1456
            .provides
1457
            .iter()
1458
            .flat_map(|x| &x.list().unwrap().args[1..])
4,314✔
1459
            .cloned()
1460
            .map(|x| (x.clone(), x))
75,093✔
1461
            .collect();
1462

1463
        for provide in &mut provides {
51,674✔
1464
            match &provide.1 {
25,031✔
1465
                ExprKind::List(l) => {
578✔
1466
                    if let Some(qualifier) = l.first_ident() {
1,156✔
1467
                        match qualifier {
1468
                            x if *x == *REQUIRE_IDENT_SPEC => {
578✔
1469
                                // *provide = expand(l.get(2).unwrap().clone(), global_macro_map)?;
1470

1471
                                // *provide = expand(l.)
1472

1473
                                provide.0 = l.get(1).unwrap().clone();
1,734✔
1474

1475
                                let mut provide_expr = l.get(2).unwrap().clone();
1,734✔
1476
                                expand(&mut provide_expr, global_macro_map)?;
1,734✔
1477

1478
                                provide.1 = provide_expr;
578✔
1479

1480
                                continue;
1481

1482
                                // name_unmangler.unmangle_expr(provide);
1483
                            }
1484
                            // x if *x == *CONTRACT_OUT => {
1485
                            //     // Update the item to point to just the name
1486
                            //     //
1487
                            //     // *provide = l.get(1).unwrap().clone();
1488
                            //     // {
1489
                            //     //     println!("---------");
1490
                            //     //     println!("Provide expr: {}", l.to_string());
1491
                            //     // }
1492

1493
                            //     provide.0 = l.get(1).unwrap().clone();
1494

1495
                            //     let mut provide_expr = expr_list![
1496
                            //         ExprKind::ident("bind/c"),
1497
                            //         l.get(2).unwrap().clone(),
1498
                            //         l.get(1).unwrap().clone(),
1499
                            //         ExprKind::Quote(Box::new(Quote::new(
1500
                            //             l.get(1).unwrap().clone(),
1501
                            //             SyntaxObject::default(TokenType::Quote)
1502
                            //         ))),
1503
                            //     ];
1504

1505
                            //     expand(&mut provide_expr, global_macro_map)?;
1506

1507
                            //     provide.1 = provide_expr;
1508

1509
                            //     name_unmangler.unmangle_expr(&mut provide.1);
1510
                            //     // continue;
1511
                            // }
1512
                            unknown => {
×
1513
                                stop!(TypeMismatch => "bar provide expects either an identifier, (for-syntax <ident>), or (contract/out ...), found: {}", unknown)
1514
                            }
1515
                        }
1516
                    } else {
1517
                        stop!(TypeMismatch => "provide expects either an identifier or a (for-syntax <ident>)")
×
1518
                    }
1519
                }
1520
                ExprKind::Atom(_) => {
1521
                    continue;
24,453✔
1522
                }
1523
                _ => {
1524
                    stop!(TypeMismatch => "provide expression needs to either be a `contract/out` form or an identifier")
×
1525
                }
1526
            }
1527
        }
1528

1529
        // Drop all of the macro references here
1530
        provides.retain(|x| !self.macro_map.contains_key(x.0.atom_identifier().unwrap()));
101,736✔
1531

1532
        // We want one without the mangled version, for the actual provides
1533
        let un_mangled = provides.clone();
1534

1535
        let left_unmangled: Vec<_> = un_mangled.into_iter().map(|x| x.0).collect();
1536

1537
        let mut right: Vec<_> = provides.into_iter().map(|x| x.1).collect();
1538

1539
        name_mangler.mangle_vars(&mut right);
1540
        // name_unmangler.unmangle_vars(&mut provides);
1541

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

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

1549
        // left_unmangled.pretty_print();
1550

1551
        hash_body.extend(interleave(
1552
            left_unmangled.into_iter().map(|x| {
24,442✔
1553
                if let ExprKind::Atom(_) = x {
24,442✔
1554
                    ExprKind::Quote(Box::new(Quote::new(
24,442✔
1555
                        x,
24,442✔
1556
                        SyntaxObject::default(TokenType::Quote),
24,442✔
1557
                    )))
1558
                } else if let ExprKind::List(l) = x {
×
1559
                    if let Some(qualifier) = l.first_ident() {
×
1560
                        match qualifier {
1561
                            x if *x == *REQUIRE_IDENT_SPEC => {
×
1562
                                todo!()
1563
                            }
1564
                            _ => {
1565
                                return ExprKind::Quote(Box::new(Quote::new(
×
1566
                                    l.get(2).unwrap().clone(),
×
1567
                                    SyntaxObject::default(TokenType::Quote),
×
1568
                                )))
1569
                            }
1570
                        }
1571
                    }
1572

1573
                    // Then this is a contract out, and we should handle it here
1574

1575
                    ExprKind::Quote(Box::new(Quote::new(
×
1576
                        l.get(2).unwrap().clone(),
×
1577
                        SyntaxObject::default(TokenType::Quote),
×
1578
                    )))
1579
                    // ExprKind::Quote(Box::new(Quote::new(
1580
                    //     x,
1581
                    //     SyntaxObject::default(TokenType::Quote),
1582
                    // )))
1583
                } else {
1584
                    panic!("TODO this shouldn't be possible")
×
1585
                }
1586
            }),
1587
            right,
1588
        ));
1589

1590
        let module_define = ExprKind::Define(Box::new(Define::new(
1591
            ExprKind::atom(CompactString::new(MODULE_PREFIX) + &prefix),
1592
            ExprKind::List(List::new(hash_body)),
1593
            SyntaxObject::default(TokenType::Quote),
1594
        )));
1595

1596
        // let mut offset = None;
1597

1598
        // Find offset of first non builtin require definition:
1599
        // for (idx, expr) in exprs.iter().enumerate() {
1600
        //     if let ExprKind::Define(d) = expr {
1601
        //         // if !is_a_builtin_definition(d) || !is_a_require_definition(d) {
1602
        //         if !is_a_builtin_definition(d) {
1603
        //             // println!("Found offset at: {:?}", offset);
1604

1605
        //             offset = Some(idx);
1606
        //             println!("Found offset at: {:?}", offset);
1607
        //             break;
1608
        //         }
1609
        //     }
1610
        // }
1611

1612
        exprs.push(module_define);
1613

1614
        // exprs.append(&mut provide_definitions);
1615

1616
        let mut builtin_definitions = Vec::new();
1617

1618
        exprs.retain_mut(|expr| {
424,054✔
1619
            if let ExprKind::Define(d) = expr {
837,373✔
1620
                if is_a_builtin_definition(d) {
1621
                    builtin_definitions.push(std::mem::take(expr));
1,457,004✔
1622
                    false
364,251✔
1623
                } else {
1624
                    true
49,068✔
1625
                }
1626
            } else {
1627
                true
10,735✔
1628
            }
1629
        });
1630

1631
        builtin_definitions.append(&mut provide_definitions);
1632
        builtin_definitions.append(&mut exprs);
1633

1634
        // provide_definitions.append(&mut builtin_definitions);
1635
        // provide_definitions.append(&mut exprs);
1636

1637
        exprs = builtin_definitions;
1638

1639
        // if let Some(offset) = offset {
1640
        // for (idx, expr) in provide_definitions.into_iter().enumerate() {
1641
        //     exprs.insert(offset + idx, expr);
1642
        // }
1643
        // } else {
1644
        // provide_definitions.append(&mut exprs);
1645
        // }
1646

1647
        // println!("MODULE DEFINITIONS----");
1648

1649
        // exprs.pretty_print();
1650

1651
        // println!("END MODULE DEFINITIONS");
1652

1653
        // exprs.pretty_print();
1654

1655
        // exprs.push(module_define);
1656

1657
        // Construct the overall definition
1658
        // TODO: Perhaps mangle these as well, especially if they have contracts associated with them
1659

1660
        // if offset.is_none() {
1661
        //     provide_definitions.append(&mut exprs);
1662
        // }
1663

1664
        // Try this out?
1665
        // let mut analysis = Analysis::from_exprs(&provide_definitions);
1666
        // let mut semantic = SemanticAnalysis::from_analysis(&mut provide_definitions, analysis);
1667

1668
        // // This is definitely broken still
1669
        // semantic.remove_unused_globals_with_prefix("mangler");
1670
        // .replace_non_shadowed_globals_with_builtins()
1671
        // .remove_unused_globals_with_prefix("mangler");
1672

1673
        Ok(ExprKind::Begin(Box::new(Begin::new(
1674
            exprs,
1675
            SyntaxObject::default(TokenType::Begin),
1676
        ))))
1677
    }
1678

1679
    // Turn the module into the AST node that represents the macro module in the stdlib
1680
    fn _to_module_ast_node(&self) -> ExprKind {
×
1681
        let mut body = vec![
×
1682
            ExprKind::Atom(Atom::new(SyntaxObject::default(TokenType::Identifier(
×
1683
                "module".into(),
×
1684
            )))),
1685
            ExprKind::Atom(Atom::new(SyntaxObject::default(TokenType::Identifier(
×
1686
                ("___".to_string() + self.name.to_str().unwrap()).into(),
×
1687
            )))),
1688
        ];
1689

1690
        // Put any provides at the top
1691
        body.append(&mut self.provides.clone());
×
1692

1693
        // Include any dependencies here
1694
        // body.append(&mut self.requires.clone());
1695

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

1702
        // self.ast.pretty_print();
1703

1704
        // body.push(steel_base);
1705

1706
        // Put the ast nodes inside the macro
1707
        body.append(&mut self.ast.clone());
×
1708

1709
        // TODO clean this up
1710
        let res = ExprKind::List(List::new(body));
×
1711

1712
        // if log_enabled!(target: "requires", log::Level::Debug) {
1713
        //     debug!(target: "requires", "Module ast node: {}", res.to_string());
1714
        // }
1715

1716
        res
×
1717
    }
1718
}
1719

1720
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
1721
enum MaybeRenamed {
1722
    Normal(ExprKind),
1723
    Renamed(ExprKind, ExprKind),
1724
}
1725

1726
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1727
pub struct RequireObject {
1728
    path: PathOrBuiltIn,
1729
    for_syntax: bool,
1730
    idents_to_import: Vec<MaybeRenamed>,
1731
    prefix: Option<String>,
1732
    span: Span,
1733
}
1734

1735
impl RequireObject {
1736
    fn as_identifiers(&self) -> Vec<InternedString> {
2,989✔
1737
        let mut out = Vec::new();
5,978✔
1738
        for identifier in &self.idents_to_import {
2,989✔
1739
            if let MaybeRenamed::Normal(n) = identifier {
×
1740
                if let Some(ident) = n.atom_identifier() {
×
1741
                    if let Some(prefix) = self.prefix.as_ref() {
×
1742
                        out.push((prefix.clone() + ident.resolve()).into());
1743
                    }
1744
                }
1745
            }
1746
        }
1747

1748
        out
2,989✔
1749
    }
1750
}
1751

1752
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1753
enum PathOrBuiltIn {
1754
    BuiltIn(Cow<'static, str>),
1755
    Path(PathBuf),
1756
}
1757

1758
impl PathOrBuiltIn {
1759
    pub fn get_path(&self) -> Cow<'_, PathBuf> {
15,070✔
1760
        match self {
15,070✔
1761
            Self::Path(p) => Cow::Borrowed(p),
880✔
1762
            Self::BuiltIn(p) => Cow::Owned(PathBuf::from(p.to_string())),
14,630✔
1763
        }
1764
    }
1765
}
1766

1767
#[derive(Default, Debug, Clone)]
1768
struct RequireObjectBuilder {
1769
    path: Option<PathOrBuiltIn>,
1770
    for_syntax: bool,
1771
    idents_to_import: Vec<MaybeRenamed>,
1772
    // Built up prefix
1773
    prefix: Option<String>,
1774
    span: Span,
1775
}
1776

1777
impl RequireObjectBuilder {
1778
    fn build(self) -> Result<RequireObject> {
4,027✔
1779
        let path = self
8,054✔
1780
            .path
4,027✔
1781
            .ok_or_else(crate::throw!(Generic => "require must have a path!"))?;
8,054✔
1782

1783
        Ok(RequireObject {
1784
            path,
1785
            for_syntax: self.for_syntax,
1786
            idents_to_import: self.idents_to_import,
1787
            prefix: self.prefix,
1788
            span: self.span,
1789
        })
1790
    }
1791
}
1792

1793
fn try_canonicalize(path: PathBuf) -> PathBuf {
218✔
1794
    std::fs::canonicalize(&path).unwrap_or_else(|_| path)
654✔
1795
}
1796

1797
/*
1798
#[derive(Default, Clone)]
1799
struct DependencyGraph {
1800
    downstream: HashMap<PathBuf, Vec<PathBuf>>,
1801
}
1802

1803
impl DependencyGraph {
1804
    // Adding edges downward.
1805
    pub fn add_edges(&mut self, parent: PathBuf, children: Vec<PathBuf>) {
1806
        self.downstream.insert(parent, children);
1807
    }
1808

1809
    pub fn remove(&mut self, parent: &PathBuf) {
1810
        self.downstream.remove(parent);
1811
    }
1812

1813
    pub fn add_edge(&mut self, parent: &PathBuf, child: PathBuf) {
1814
        if let Some(children) = self.downstream.get_mut(parent) {
1815
            children.push(child);
1816
        } else {
1817
            self.downstream.insert(parent.clone(), vec![child]);
1818
        }
1819
    }
1820

1821
    // Check everything downstream of this, to see if anything needs to be invalidated
1822
    pub fn check_downstream_changes(
1823
        &self,
1824
        root: &PathBuf,
1825
        updated_at: &crate::HashMap<PathBuf, SystemTime>,
1826
    ) -> std::io::Result<bool> {
1827
        let mut stack = vec![root];
1828

1829
        while let Some(next) = stack.pop() {
1830
            let meta = std::fs::metadata(next)?;
1831

1832
            if let Some(prev) = updated_at.get(next) {
1833
                if *prev != meta.modified()? {
1834
                    return Ok(true);
1835
                }
1836
            }
1837

1838
            if let Some(children) = self.downstream.get(next) {
1839
                for child in children {
1840
                    stack.push(child);
1841
                }
1842
            }
1843
        }
1844

1845
        Ok(false)
1846
    }
1847
}
1848
*/
1849

1850
struct ModuleBuilder<'a> {
1851
    name: PathBuf,
1852
    main: bool,
1853
    source_ast: Vec<ExprKind>,
1854
    local_macros: FxHashSet<InternedString>,
1855
    macro_map: Arc<FxHashMap<InternedString, SteelMacro>>,
1856
    // TODO: Change the requires / requires_for_syntax to just be a require enum?
1857
    require_objects: Vec<RequireObject>,
1858

1859
    provides: Vec<ExprKind>,
1860
    provides_for_syntax: Vec<ExprKind>,
1861
    compiled_modules: &'a mut crate::HashMap<PathBuf, CompiledModule>,
1862
    visited: &'a mut FxHashSet<PathBuf>,
1863
    file_metadata: &'a mut crate::HashMap<PathBuf, SystemTime>,
1864
    sources: &'a mut Sources,
1865
    kernel: &'a mut Option<Kernel>,
1866
    builtin_modules: ModuleContainer,
1867
    global_macro_map: &'a FxHashMap<InternedString, SteelMacro>,
1868
    custom_builtins: &'a HashMap<String, String>,
1869
    search_dirs: &'a [PathBuf],
1870
    module_resolvers: &'a [Arc<dyn SourceModuleResolver>],
1871
}
1872

1873
pub struct RequiredModuleMacros {
1874
    pub module_key: PathBuf,
1875
    pub module_name: String,
1876
    pub module_macros: Arc<FxHashMap<InternedString, SteelMacro>>,
1877
    pub in_scope_macros: FxHashMap<InternedString, SteelMacro>,
1878
    pub name_mangler: NameMangler,
1879
}
1880

1881
impl<'a> ModuleBuilder<'a> {
1882
    #[allow(clippy::too_many_arguments)]
1883
    #[allow(unused)]
1884
    fn main(
11,489✔
1885
        name: Option<PathBuf>,
1886
        source_ast: Vec<ExprKind>,
1887
        compiled_modules: &'a mut crate::HashMap<PathBuf, CompiledModule>,
1888
        visited: &'a mut FxHashSet<PathBuf>,
1889
        file_metadata: &'a mut crate::HashMap<PathBuf, SystemTime>,
1890
        sources: &'a mut Sources,
1891
        kernel: &'a mut Option<Kernel>,
1892
        builtin_modules: ModuleContainer,
1893
        global_macro_map: &'a FxHashMap<InternedString, SteelMacro>,
1894
        custom_builtins: &'a HashMap<String, String>,
1895
        search_dirs: &'a [PathBuf],
1896
        module_resolvers: &'a [Arc<dyn SourceModuleResolver>],
1897
    ) -> Result<Self> {
1898
        // TODO don't immediately canonicalize the path unless we _know_ its coming from a path
1899
        // change the path to not always be required
1900
        // if its not required we know its not coming in
1901

1902
        #[cfg(not(target_family = "wasm"))]
1903
        let name = if let Some(p) = name {
22,981✔
1904
            std::fs::canonicalize(p)?
×
1905
        } else {
1906
            std::env::current_dir()?
11,486✔
1907
        };
1908

1909
        #[cfg(target_family = "wasm")]
1910
        let name = PathBuf::new();
×
1911

1912
        Ok(ModuleBuilder {
×
1913
            name,
×
1914
            main: true,
×
1915
            source_ast,
×
1916
            local_macros: FxHashSet::default(),
×
1917
            macro_map: default_prelude_macros(),
×
1918
            require_objects: Vec::new(),
×
1919
            provides: Vec::new(),
×
1920
            provides_for_syntax: Vec::new(),
×
1921
            compiled_modules,
×
1922
            visited,
×
1923
            file_metadata,
×
1924
            sources,
×
1925
            kernel,
×
1926
            builtin_modules,
×
1927
            global_macro_map,
×
1928
            custom_builtins,
×
1929
            search_dirs,
×
1930
            module_resolvers,
×
1931
        })
1932
    }
1933

1934
    fn compile(&mut self) -> Result<Vec<ExprKind>> {
13,101✔
1935
        // debug!(target: "requires", "Visiting: {:?}", self.name);
1936

1937
        // @Matt - 10/3/23
1938
        // This has a relatively fatal flaw at the moment:
1939
        /*
1940

1941
            (define-syntax register-plugin
1942
              (syntax-rules ()
1943
                [(register-plugin plugin-path identifiers ...)
1944
                 (begin
1945
                   (require plugin-path
1946
                            (only-in identifiers ...))
1947
                   (provide identifiers ...))]))
1948
        */
1949
        // This will fail to compile - anything that expands into a require will fail since
1950
        // require is more or less a top level primitive. What we need to do is figure
1951
        // out a way to have this be recursive - the whole compilation step should
1952
        // go again once we discover that there is another require. It shouldn't be too bad,
1953
        // but the base case has to be figured out such that we don't get infinite recursion.
1954
        // I think the condition is probably something along the following:
1955
        //
1956
        // - Did we expand anything
1957
        // - Are there require statements
1958
        //
1959
        // If we expanded anything, we then should check for require statements... maybe
1960

1961
        let mut new_exprs = Vec::new();
26,202✔
1962

1963
        // self.source_ast.pretty_print();
1964

1965
        self.collect_requires()?;
26,202✔
1966
        self.collect_provides()?;
13,101✔
1967

1968
        // if log_enabled!(log::Level::Info) {
1969
        // debug!(target: "requires", "Requires: {:#?}", self.require_objects);
1970
        // debug!(target: "requires", "Provides: {:#?}", self.provides);
1971
        // debug!(target: "requires", "Provides for-syntax: {:?}", self.provides_for_syntax);
1972
        // }
1973

1974
        if self.visited.contains(&self.name) {
13,101✔
1975
            stop!(Generic => format!("circular dependency found during module resolution with: {:?}", self.name))
×
1976
        }
1977

1978
        self.visited.insert(self.name.clone());
×
1979

1980
        if self.main {
×
1981
            let exprs = std::mem::take(&mut self.source_ast);
34,467✔
1982
            self.source_ast = exprs
22,978✔
1983
                .into_iter()
11,489✔
1984
                .filter(|x| {
37,438✔
1985
                    if let ExprKind::List(l) = x {
51,869✔
1986
                        if let Some(provide) = l.first_ident() {
2,452✔
1987
                            return *provide != *PROVIDE;
×
1988
                        }
1989
                    }
1990
                    true
23,497✔
1991
                })
1992
                .collect();
11,489✔
1993
        }
1994

1995
        self.extract_macro_defs()?;
×
1996

1997
        // TODO include built ins here
1998
        if self.require_objects.is_empty() && !self.main {
25,098✔
1999
            // We're at a leaf, put into the cache
2000
            new_exprs.push(self.compile_module()?);
3,036✔
2001
        } else {
2002
            // TODO come back for parsing built ins
2003
            for module in self
16,248✔
2004
                .require_objects
12,342✔
2005
                .iter()
12,342✔
2006
                .filter(|x| matches!(x.path, PathOrBuiltIn::BuiltIn(_)))
4,027✔
2007
                .map(|x| x.path.get_path())
7,812✔
2008
            {
2009
                // We've established nothing has changed with this file
2010
                // Check to see if its in the cache first
2011
                // Otherwise go ahead and compile
2012
                // If we already have compiled this module, get it from the cache
2013
                if let Some(_m) = self.compiled_modules.get(module.as_ref()) {
14,109✔
2014
                    // debug!("Getting {:?} from the module cache", module);
2015
                    // println!("Already found in the cache: {:?}", module);
2016
                    // new_exprs.push(m.to_module_ast_node());
2017
                    // No need to do anything
2018
                    continue;
×
2019
                }
2020

2021
                // TODO this is some bad crap here don't do this
2022
                let input = BUILT_INS
3,030✔
2023
                    .iter()
2024
                    .find(|x| x.0 == module.to_str().unwrap())
26,111✔
2025
                    .map(|x| Cow::Borrowed(x.1))
3,030✔
2026
                    .or_else(|| {
1,515✔
2027
                        self.custom_builtins
×
2028
                            .get(module.to_str().unwrap())
×
2029
                            .map(|x| Cow::Owned(x.to_string()))
×
2030
                    })
2031
                    .or_else(|| {
1,515✔
2032
                        self.module_resolvers
×
2033
                            .iter()
×
2034
                            .find_map(|x| x.resolve(module.to_str().unwrap()))
×
2035
                            // Insert the prelude
2036
                            .map(|mut x| {
×
2037
                                x.insert_str(0, PRELUDE_STRING);
×
2038
                                x
×
2039
                            })
2040
                            .map(Cow::Owned)
×
2041
                    })
2042
                    .ok_or_else(
2043
                        crate::throw!(Generic => "Unable to find builtin module: {:?}", module),
1,515✔
2044
                    )?;
2045

2046
                let mut new_module = ModuleBuilder::new_built_in(
2047
                    module.into_owned(),
×
2048
                    input,
×
2049
                    self.compiled_modules,
×
2050
                    self.visited,
×
2051
                    self.file_metadata,
×
2052
                    self.sources,
×
2053
                    self.kernel,
×
2054
                    self.builtin_modules.clone(),
×
2055
                    self.global_macro_map,
×
2056
                    self.custom_builtins,
×
2057
                    self.module_resolvers,
×
2058
                )?;
2059

2060
                // Walk the tree and compile any dependencies
2061
                // This will eventually put the module in the cache
2062
                let mut module_exprs = new_module.compile()?;
1,515✔
2063

2064
                new_exprs.append(&mut module_exprs);
×
2065

2066
                // Probably want to evaluate a module even if it has no provides?
2067
                if !new_module.provides.is_empty() {
×
2068
                    new_exprs.push(new_module.compile_module()?);
1,512✔
2069
                } else {
2070
                    // log::debug!(target: "requires", "Found no provides, skipping compilation of module: {:?}", new_module.name);
2071
                }
2072
            }
2073

2074
            // At this point, requires should be fully qualified (absolute) paths
2075

2076
            for (module, require_statement_span) in self
12,584✔
2077
                .require_objects
12,342✔
2078
                .iter()
12,342✔
2079
                .filter(|x| matches!(x.path, PathOrBuiltIn::Path(_)))
4,027✔
2080
                .map(|x| (x.path.get_path(), x.span))
363✔
2081
            {
2082
                if cfg!(target_family = "wasm") {
2083
                    stop!(Generic => "requiring modules is not supported for wasm");
×
2084
                }
2085

2086
                let last_modified = std::fs::metadata(module.as_ref())
484✔
2087
                    .map_err(|err| {
121✔
2088
                        let mut err = crate::SteelErr::from(err);
×
2089
                        err.prepend_message(&format!(
×
2090
                            "Attempting to load module from: {:?} ",
×
2091
                            module
×
2092
                        ));
2093
                        err.set_span(require_statement_span)
×
2094
                    })?
2095
                    .modified()?;
2096

2097
                // Check if we should compile based on the last time modified
2098
                // If we're unable to get information, we want to compile
2099
                let should_recompile =
×
2100
                    if let Some(cached_modified) = self.file_metadata.get(module.as_ref()) {
24✔
2101
                        last_modified != *cached_modified
×
2102
                    } else {
2103
                        true
97✔
2104
                    };
2105

2106
                let mut downstream_validated = true;
×
2107

2108
                // We've established nothing has changed with this file
2109
                // Check to see if its in the cache first
2110
                // Otherwise go ahead and compile
2111
                if !should_recompile {
×
2112
                    // If we already have compiled this module, get it from the cache
2113
                    if let Some(m) = self.compiled_modules.get(module.as_ref()) {
48✔
2114
                        // debug!("Getting {:?} from the module cache", module);
2115
                        // println!("Already found in the cache: {:?}", module);
2116
                        // new_exprs.push(m.to_module_ast_node());
2117
                        // No need to do anything
2118

2119
                        // Check the dependencies all the way down.
2120
                        // if any are invalidated, re-compile the whole thing.
2121
                        let mut stack = m.downstream.clone();
×
2122

2123
                        while let Some(next) = stack.pop() {
74✔
2124
                            let meta = std::fs::metadata(&next)?;
25✔
2125

2126
                            if let Some(prev) = self.file_metadata.get(&next) {
25✔
2127
                                if *prev != meta.modified()? {
×
2128
                                    // println!(
2129
                                    //     "Detected change in {:?}, recompiling root starting from {:?}",
2130
                                    //     next,
2131
                                    //     module.as_ref()
2132
                                    // );
2133
                                    downstream_validated = false;
×
2134
                                    break;
×
2135
                                }
2136
                            }
2137

2138
                            if let Some(module) = self.compiled_modules.get(&next) {
50✔
2139
                                for child in &module.downstream {
49✔
2140
                                    stack.push(child.to_owned());
×
2141
                                }
2142
                            }
2143
                        }
2144

2145
                        if downstream_validated {
24✔
2146
                            continue;
24✔
2147
                        }
2148
                    }
2149
                }
2150

2151
                let mut new_module = ModuleBuilder::new_from_path(
2152
                    module.into_owned(),
×
2153
                    self.compiled_modules,
×
2154
                    self.visited,
×
2155
                    self.file_metadata,
×
2156
                    self.sources,
×
2157
                    self.kernel,
×
2158
                    self.builtin_modules.clone(),
×
2159
                    self.global_macro_map,
×
2160
                    self.custom_builtins,
×
2161
                    self.search_dirs,
×
2162
                    self.module_resolvers,
×
2163
                )?;
2164

2165
                // Walk the tree and compile any dependencies
2166
                // This will eventually put the module in the cache
2167
                let mut module_exprs = new_module.compile()?;
97✔
2168

2169
                // debug!("Inside {:?} - append {:?}", self.name, module);
2170
                // if log_enabled!(log::Level::Debug) {
2171
                //     debug!(
2172
                //         target: "modules",
2173
                //         "appending with {:?}",
2174
                //         module_exprs.iter().map(|x| x.to_string()).join(" SEP ")
2175
                //     );
2176
                // }
2177

2178
                new_exprs.append(&mut module_exprs);
×
2179

2180
                // TODO evaluate this
2181

2182
                // let mut ast = std::mem::replace(&mut new_module.source_ast, Vec::new());
2183
                // ast.append(&mut module_exprs);
2184
                // new_module.source_ast = ast;
2185

2186
                // dbg!(&new_module.name);
2187
                // dbg!(&new_module.compiled_modules.contains_key(&new_module.name));
2188

2189
                // If we need to, revisit because there are new provides
2190
                if !new_module.provides.is_empty() {
×
2191
                    new_exprs.push(new_module.compile_module()?);
142✔
2192
                // If the module hasn't yet been compiled, compile it anyway
2193
                } else if !new_module.compiled_modules.contains_key(&new_module.name) {
52✔
2194
                    // else if !new_module.compiled_modules.contains_key(&new_module.name) {
2195
                    new_exprs.push(new_module.compile_module()?);
52✔
2196
                } else if !downstream_validated {
×
2197
                    new_exprs.push(new_module.compile_module()?);
×
2198

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

2204
                // else {
2205
                //     log::debug!(target: "requires", "Found no provides, skipping compilation of module: {:?}", new_module.name);
2206
                // }
2207
            }
2208
        }
2209

2210
        // new_exprs.pretty_print();
2211

2212
        Ok(new_exprs)
13,101✔
2213
    }
2214

2215
    // TODO: This should run again on itself, probably
2216
    fn compile_module(&mut self) -> Result<ExprKind> {
1,612✔
2217
        let mut ast = std::mem::take(&mut self.source_ast);
4,836✔
2218
        let mut provides = std::mem::take(&mut self.provides);
4,836✔
2219
        // Clone the requires... I suppose
2220
        let requires = self.require_objects.clone();
4,836✔
2221

2222
        // info!(
2223
        //     target: "requires",
2224
        //     "Into compiled module: provides for syntax: {:?}",
2225
        //     self.provides_for_syntax
2226
        // );
2227

2228
        // Attempt extracting the syntax transformers from this module
2229
        if let Some(kernel) = self.kernel.as_mut() {
3,224✔
2230
            kernel.load_syntax_transformers(&mut ast, self.name.to_str().unwrap().to_string())?
×
2231
        };
2232

2233
        // This needs to be overlayed with imported macros. So the idea being that this
2234
        // expansion _also_ interweaves with the other requires... seems to make sense.
2235
        //
2236
        // And it also seems like it needs to happen at the same time. So the lookups
2237
        // should happen together.
2238

2239
        if self.require_objects.is_empty() {
1,612✔
2240
            for expr in ast.iter_mut() {
30,099✔
2241
                expand(expr, &self.macro_map)?;
85,743✔
2242

2243
                expand_kernel_in_env(
2244
                    expr,
×
2245
                    self.kernel.as_mut(),
×
2246
                    self.builtin_modules.clone(),
×
2247
                    // Expanding macros in the environment?
2248
                    self.name.to_str().unwrap(),
×
2249
                )?;
2250

2251
                expand(expr, &self.macro_map)?;
28,581✔
2252
            }
2253
        }
2254

2255
        // TODO: Provides also need to have this kind of macro expansion as the above!
2256
        for expr in provides.iter_mut() {
3,765✔
2257
            expand(expr, &self.macro_map)?;
6,459✔
2258
            // .and_then(|x| {
2259
            // expand_kernel(x, self.kernel.as_mut(), self.builtin_modules.clone())
2260
            expand_kernel_in_env(
2261
                expr,
×
2262
                self.kernel.as_mut(),
×
2263
                self.builtin_modules.clone(),
×
2264
                // Expanding macros in the environment?
NEW
2265
                self.name.to_str().unwrap(),
×
2266
            )?;
2267
            // })
2268
        }
2269

2270
        let mut mangled_asts = Vec::with_capacity(ast.len() + 16);
1,612✔
2271
        let mut downstream = Vec::new();
×
2272

2273
        let mut required_macros = Vec::new();
×
2274

2275
        for require_object in self.require_objects.iter() {
2,263✔
2276
            let require_for_syntax = require_object.path.get_path();
×
2277

2278
            if let PathOrBuiltIn::Path(_) = &require_object.path {
64✔
2279
                downstream.push(require_for_syntax.clone().into_owned());
64✔
2280
            }
2281

2282
            // Stash this?
2283
            let (module, in_scope_macros, name_mangler) = ModuleManager::find_in_scope_macros(
2284
                self.compiled_modules,
×
2285
                require_for_syntax.as_ref(),
×
NEW
2286
                require_object,
×
2287
                &mut mangled_asts,
×
2288
            );
2289

2290
            required_macros.push(RequiredModuleMacros {
×
2291
                module_key: require_for_syntax.clone().into_owned(),
×
2292
                module_macros: module.macro_map.clone(),
×
2293
                in_scope_macros,
×
2294
                name_mangler,
×
2295
                module_name: module.name.to_str().unwrap().to_owned(),
×
2296
            });
2297
        }
2298

2299
        let overlays = required_macros
×
2300
            .iter_mut()
2301
            .map(|x| RequiredMacroMap {
×
2302
                map: x,
2,263✔
2303
                changed: false,
×
2304
            })
2305
            .collect::<Vec<_>>();
2306

2307
        let mut many_expander = ExpanderMany::new(&self.macro_map, overlays);
×
2308

2309
        for expr in ast.iter_mut() {
37,582✔
2310
            many_expander.expand(expr)?;
112,746✔
2311

2312
            expand_kernel_in_env(
2313
                expr,
×
2314
                self.kernel.as_mut(),
×
2315
                self.builtin_modules.clone(),
×
2316
                // Expanding macros in the environment?
2317
                self.name.to_str().unwrap(),
×
2318
            )?;
2319

2320
            expand(expr, &self.macro_map)?;
37,582✔
2321

2322
            for RequiredMacroMap {
×
2323
                changed,
33,578✔
2324
                map: req_macro,
×
2325
            } in many_expander.overlays.iter_mut()
37,582✔
2326
            {
2327
                if *changed {
×
2328
                    expand(expr, &req_macro.module_macros)?;
6,663✔
2329

2330
                    // Expanding the kernel with only these macros...
2331
                    let local_changed = expand_kernel_in_env_with_change(
2332
                        expr,
×
2333
                        self.kernel.as_mut(),
×
2334
                        // We don't need to expand those here
2335
                        ModuleContainer::default(),
×
2336
                        &req_macro.module_name,
×
2337
                    )?;
2338

2339
                    if local_changed {
17✔
2340
                        req_macro.name_mangler.visit(expr);
34✔
2341
                    }
2342

2343
                    *changed = false;
×
2344
                }
2345
            }
2346
        }
2347

2348
        for req_macro in &mut required_macros {
6,138✔
2349
            for expr in provides.iter_mut() {
6,530✔
2350
                // First expand the in scope macros
2351
                // These are macros
2352
                let mut expander = Expander::new(&req_macro.in_scope_macros);
×
2353
                expander.expand(expr)?;
×
2354

2355
                if expander.changed {
2,004✔
2356
                    expand(expr, &req_macro.module_macros)?
570✔
2357
                }
2358
            }
2359
        }
2360

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

2364
        for expr in &mut ast {
76,776✔
2365
            lower_entire_ast(expr)?;
75,164✔
2366
            FlattenBegin::flatten(expr);
37,582✔
2367
        }
2368

2369
        // TODO: @Matt - fix this hack
2370
        {
2371
            self.source_ast = ast;
1,612✔
2372
            self.provides = provides;
×
2373

2374
            self.collect_provides()?;
×
2375

2376
            provides = std::mem::take(&mut self.provides);
1,612✔
2377
            ast = std::mem::take(&mut self.source_ast);
×
2378
        }
2379

2380
        // Put the mangled asts at the top
2381
        // then include the ast there
2382
        // mangled_asts.append(&mut ast);
2383

2384
        for expr in mangled_asts.iter_mut() {
×
2385
            lower_entire_ast(expr)?;
×
2386

2387
            FlattenBegin::flatten(expr);
×
2388
        }
2389

2390
        mangled_asts.append(&mut ast);
1,612✔
2391

2392
        // TODO: @Matt: 7/3/25
2393
        // Re-exporting macros needs to work here
2394
        let mut module = CompiledModule::new(
2395
            self.name.clone(),
×
2396
            provides,
×
2397
            requires,
×
2398
            self.provides_for_syntax
×
2399
                .iter()
×
2400
                .map(|x| *x.atom_identifier().unwrap())
2,280✔
2401
                .collect(),
×
2402
            // std::mem::take(&mut self.macro_map),
2403
            self.macro_map.clone(),
×
2404
            mangled_asts,
×
2405
            downstream,
×
2406
        );
2407

2408
        module.set_emitted(true);
×
2409

2410
        let result = module.to_top_level_module(self.compiled_modules, self.global_macro_map)?;
1,612✔
2411

2412
        self.compiled_modules.insert(self.name.clone(), module);
×
2413

2414
        Ok(result)
×
2415
    }
2416

2417
    fn extract_macro_defs(&mut self) -> Result<()> {
13,101✔
2418
        // Probably don't have more than 128 macros in a module, but who knows?
2419
        // let mut macro_indices = SmallVec::<[usize; 128]>::new();
2420

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

2423
        let mut error = None;
26,202✔
2424

2425
        self.source_ast.retain_mut(|expr| {
98,672✔
2426
            if let ExprKind::Macro(_) = expr {
72,470✔
2427
                // Replace with dummy begin value so we don't have to copy
2428
                // everything other for every macro definition
2429
                let mut taken_expr = ExprKind::Begin(Box::new(Begin::new(
8,183✔
2430
                    Vec::new(),
×
2431
                    SyntaxObject::default(TokenType::Begin),
×
2432
                )));
2433

2434
                std::mem::swap(expr, &mut taken_expr);
×
2435

2436
                if let ExprKind::Macro(m) = taken_expr {
8,183✔
2437
                    match SteelMacro::parse_from_ast_macro(m) {
8,183✔
2438
                        Ok(generated_macro) => {
8,183✔
2439
                            let name = generated_macro.name();
×
2440

2441
                            self.local_macros.insert(*name);
×
2442

2443
                            Arc::make_mut(&mut self.macro_map).insert(*name, generated_macro);
×
2444
                        }
2445
                        Err(e) => {
×
2446
                            if error.is_none() {
×
2447
                                error = Some(e);
×
2448
                            }
2449
                            // error = Some(e);
2450
                            return false;
×
2451
                        }
2452
                    }
2453
                } else {
2454
                    unreachable!();
×
2455
                }
2456

2457
                return false;
×
2458
            }
2459

2460
            true
64,287✔
2461
        });
2462

2463
        if let Some(e) = error {
13,101✔
2464
            return Err(e);
×
2465
        }
2466

2467
        Ok(())
13,101✔
2468
    }
2469

2470
    // Takes out the (for-syntax) forms from the provides
2471
    fn filter_out_for_syntax_provides(&mut self, exprs: Vec<ExprKind>) -> Result<Vec<ExprKind>> {
2,561✔
2472
        let mut normal_provides = Vec::new();
5,122✔
2473

2474
        for expr in exprs {
68,159✔
2475
            match &expr {
32,799✔
2476
                ExprKind::Atom(_) => {
31,272✔
2477
                    normal_provides.push(expr);
31,272✔
2478
                }
2479
                ExprKind::List(l) => {
1,527✔
2480
                    if let Some(for_syntax) = l.first_ident() {
3,054✔
2481
                        match *for_syntax {
578✔
2482
                            x if x == *FOR_SYNTAX => {
1,898✔
2483
                                if l.args.len() != 2 {
949✔
2484
                                    stop!(ArityMismatch => "provide expects a single identifier in the (for-syntax <ident>)"; l.location)
×
2485
                                }
2486

2487
                                // Collect the for syntax expressions
2488
                                // TODO -> remove this clone
2489
                                self.provides_for_syntax.push(l.args[1].clone());
×
2490
                            }
2491
                            x if x == *REQUIRE_IDENT_SPEC => {
6✔
2492
                                normal_provides.push(expr);
4✔
2493
                            }
2494
                            // x if x == *CONTRACT_OUT || x == *REQUIRE_IDENT_SPEC => {
2495
                            //     normal_provides.push(expr);
2496
                            // }
2497
                            _ => {
576✔
2498
                                normal_provides.push(expr);
576✔
2499
                                // stop!(TypeMismatch => "provide expects either an identifier, (for-syntax <ident>), or (contract/out ...)")
2500
                            }
2501
                        }
2502
                    } else {
2503
                        stop!(TypeMismatch => "provide expects either an identifier or a (for-syntax <ident>)"; l.location)
×
2504
                    }
2505
                }
2506
                _ => {
×
2507
                    stop!(TypeMismatch => "provide expects either a (for-syntax <ident>) or an ident"; expr.span())
×
2508
                }
2509
            }
2510
        }
2511

2512
        Ok(normal_provides)
2,561✔
2513
    }
2514

2515
    // TODO -> collect (provide (for-syntax ...))
2516
    // I think these will already be collected for the macro, however I think for syntax should be found earlier
2517
    // Otherwise the macro expansion will not be able to understand it
2518
    fn collect_provides(&mut self) -> Result<()> {
26,202✔
2519
        // let now = std::time::Instant::now();
2520

2521
        let mut non_provides = Vec::with_capacity(self.source_ast.len());
104,808✔
2522
        let exprs = std::mem::take(&mut self.source_ast);
78,606✔
2523

2524
        fn walk(
30,952✔
2525
            module_builder: &mut ModuleBuilder,
2526
            exprs: Vec<ExprKind>,
2527
            non_provides: &mut Vec<ExprKind>,
2528
        ) -> Result<()> {
2529
            for mut expr in exprs {
1,077,796✔
2530
                match &mut expr {
523,422✔
2531
                    ExprKind::List(l) => {
96,993✔
2532
                        if let Some(provide) = l.first_ident() {
115,060✔
2533
                            if *provide == *PROVIDE {
×
2534
                                if l.len() == 1 {
2,561✔
2535
                                    stop!(Generic => "provide expects at least one identifier to provide"; l.location);
×
2536
                                }
2537

2538
                                // Swap out the value inside the list
2539
                                let args = std::mem::take(&mut l.args);
×
2540

2541
                                let filtered =
2,561✔
2542
                                    module_builder.filter_out_for_syntax_provides(args)?;
×
2543

2544
                                l.args = filtered;
×
2545

2546
                                module_builder.provides.push(expr);
×
2547

2548
                                continue;
×
2549
                            }
2550
                        }
2551
                    }
2552
                    ExprKind::Begin(b) => {
4,750✔
2553
                        let exprs = std::mem::take(&mut b.exprs);
×
2554

2555
                        // Reserve capacity for these to be moved to the top level
2556
                        non_provides.reserve(exprs.len());
×
2557

2558
                        walk(module_builder, exprs, non_provides)?;
×
2559
                    }
2560
                    _ => {}
421,679✔
2561
                }
2562

2563
                non_provides.push(expr);
520,861✔
2564
            }
2565

2566
            Ok(())
30,952✔
2567
        }
2568

2569
        walk(self, exprs, &mut non_provides)?;
104,808✔
2570

2571
        self.source_ast = non_provides;
26,202✔
2572

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

2575
        Ok(())
×
2576
    }
2577

2578
    fn parse_require_object(
4,027✔
2579
        &mut self,
2580
        home: &Option<PathBuf>,
2581
        r: &crate::parser::ast::Require,
2582
        atom: &ExprKind,
2583
    ) -> Result<RequireObject> {
2584
        let mut object = RequireObjectBuilder::default();
8,054✔
2585

2586
        // Set the span so we can pass through an error if the
2587
        // module is not found
2588
        object.span = r.location.span;
4,027✔
2589

2590
        self.parse_require_object_inner(home, r, atom, &mut object)
24,162✔
2591
            .and_then(|_| object.build())
12,081✔
2592
    }
2593

2594
    // TODO: Recursively crunch the requires to gather up the necessary information
2595
    fn parse_require_object_inner(
4,031✔
2596
        &mut self,
2597
        home: &Option<PathBuf>,
2598
        r: &crate::parser::ast::Require,
2599
        atom: &ExprKind,
2600
        require_object: &mut RequireObjectBuilder,
2601
    ) -> Result<()> {
2602
        match atom {
3,068✔
2603
            ExprKind::Atom(Atom {
×
2604
                syn:
×
2605
                    SyntaxObject {
×
2606
                        ty: TokenType::StringLiteral(s),
3,068✔
2607
                        span,
×
2608
                        ..
×
2609
                    },
2610
            }) => {
×
2611
                if require_object.path.is_some() {
×
2612
                    stop!(Generic => "require object only expects one path!")
×
2613
                }
2614

2615
                // Try this?
2616
                if let Some(lib) = BUILT_INS.into_iter().cloned().find(|x| x.0 == s.as_str()) {
54,960✔
2617
                    // self.built_ins.push(PathBuf::from(lib.0));
2618

2619
                    require_object.path = Some(PathOrBuiltIn::BuiltIn(lib.0.into()));
×
2620

2621
                    return Ok(());
×
2622
                    // continue;
2623
                }
2624

2625
                if self.custom_builtins.contains_key(s.as_str()) {
336✔
2626
                    require_object.path =
×
2627
                        Some(PathOrBuiltIn::BuiltIn(s.clone().to_string().into()));
×
2628

2629
                    return Ok(());
×
2630
                }
2631

2632
                if self
×
2633
                    .module_resolvers
×
2634
                    .iter()
2635
                    .find(|x| x.exists(s.as_str()))
×
2636
                    .is_some()
2637
                {
2638
                    require_object.path =
×
2639
                        Some(PathOrBuiltIn::BuiltIn(s.clone().to_string().into()));
×
2640

2641
                    return Ok(());
×
2642
                }
2643

2644
                if cfg!(target_family = "wasm") {
2645
                    stop!(Generic => "requiring modules is not supported for wasm");
×
2646
                }
2647

2648
                let mut current = self.name.clone();
×
2649
                if current.is_file() {
58✔
2650
                    current.pop();
58✔
2651
                }
2652
                current.push(PathBuf::from(s.as_str()));
×
2653

2654
                // // If the path exists on its own, we can continue
2655
                // // But theres the case where we're searching for a module on the STEEL_HOME
2656
                if !current.exists() {
×
2657
                    if let Some(mut home) = home.clone() {
42✔
2658
                        home.push(PathBuf::from(s.as_str()));
×
2659
                        current = home;
×
2660

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

2663
                        if !current.exists() {
×
2664
                            for dir in self.search_dirs {
×
2665
                                let mut dir = dir.clone();
×
2666
                                dir.push(s.as_str());
×
2667

2668
                                if dir.exists() {
×
2669
                                    current = dir;
×
2670
                                    break;
×
2671
                                }
2672
                            }
2673
                        }
2674
                    } else {
2675
                        // TODO: Check if this module exists in STEEL_HOME first. If it does, we'll take that as our candidate
2676
                        // and then continue on to the final module resolution part.
2677
                        //
2678
                        // If it doesn't exist, we should iterate through the search directories and attempt to find
2679
                        // a matching path there.
2680

2681
                        for dir in self.search_dirs {
×
2682
                            let mut dir = dir.clone();
×
2683
                            dir.push(s.as_str());
×
2684

2685
                            if dir.exists() {
×
2686
                                current = dir;
×
2687
                                break;
×
2688
                            }
2689
                        }
2690

2691
                        stop!(Generic => format!("Module not found: {:?} with STEEL_HOME: {:?}", current, home); *span)
×
2692
                    }
2693
                }
2694

2695
                // Get the absolute path and store that
2696
                // self.requires.push(current)
2697

2698
                let current = try_canonicalize(current);
112✔
2699

2700
                require_object.path = Some(PathOrBuiltIn::Path(current));
×
2701
            }
2702

2703
            // TODO: Requires with qualifiers, that aren't just for-syntax
2704
            // Perhaps like:
2705
            // (with-prefix <xyz>)
2706
            ExprKind::List(l) => {
963✔
2707
                match l.first_ident() {
1,926✔
2708
                    Some(x) if *x == *ONLY_IN => {
969✔
2709
                        if l.args.len() < 2 {
3✔
2710
                            stop!(BadSyntax => "only-in expects a require-spec and optionally a list of ids to bind (maybe renamed)"; l.location);
×
2711
                        }
2712

2713
                        self.parse_require_object_inner(home, r, &l.args[1], require_object)?;
×
2714

2715
                        for remaining in &l.args[2..] {
12✔
2716
                            match remaining {
9✔
2717
                                ExprKind::Atom(_) => {
7✔
2718
                                    require_object
7✔
2719
                                        .idents_to_import
7✔
2720
                                        .push(MaybeRenamed::Normal(remaining.clone()));
7✔
2721
                                }
2722
                                ExprKind::List(l) => {
2✔
2723
                                    if l.len() != 2 {
2✔
2724
                                        stop!(BadSyntax => "Expected a pair when renaming required identifiers");
×
2725
                                    }
2726

2727
                                    let from = &l.args[0];
×
2728
                                    let to = &l.args[1];
×
2729

2730
                                    if from.atom_identifier().is_none()
×
2731
                                        || to.atom_identifier().is_none()
2✔
2732
                                    {
2733
                                        stop!(BadSyntax => "only-in expected identifiers to rename");
×
2734
                                    }
2735

2736
                                    // (<from> <to>)
2737
                                    require_object
×
2738
                                        .idents_to_import
×
2739
                                        .push(MaybeRenamed::Renamed(from.clone(), to.clone()));
×
2740
                                }
2741
                                _ => {
×
2742
                                    stop!(BadSyntax => "unexpected syntax in only-in form during module requires")
×
2743
                                }
2744
                            }
2745
                        }
2746
                    }
2747

2748
                    Some(x) if *x == *PREFIX_IN => {
962✔
2749
                        if l.args.len() != 3 {
1✔
2750
                            stop!(BadSyntax => "prefix-in expects a prefix to prefix a given file or module"; l.location);
×
2751
                        }
2752

2753
                        let prefix = &l.args[1];
×
2754

2755
                        if let Some(prefix) = prefix.atom_identifier() {
1✔
2756
                            match &mut require_object.prefix {
×
2757
                                Some(existing_prefix) => {
×
2758
                                    // Append the new symbol to the existing prefix
2759
                                    existing_prefix.push_str(prefix.resolve());
×
2760
                                }
2761
                                None => {
1✔
2762
                                    require_object.prefix = Some(prefix.resolve().to_string());
1✔
2763
                                }
2764
                            }
2765

2766
                            self.parse_require_object_inner(home, r, &l.args[2], require_object)?;
×
2767
                        } else {
2768
                            stop!(TypeMismatch => "prefix-in expects an identifier to use for the prefix"; prefix.span());
×
2769
                        }
2770
                    }
2771

2772
                    Some(x) if *x == *FOR_SYNTAX => {
2,877✔
2773
                        // We're expecting something like (for-syntax "foo")
2774
                        if l.args.len() != 2 {
959✔
2775
                            stop!(BadSyntax => "for-syntax expects one string literal referring to a file or module"; l.location);
×
2776
                        }
2777

2778
                        let mod_name = &l.args[1];
×
2779
                        if let Some(path) = mod_name.string_literal() {
959✔
2780
                            if let Some(lib) = BUILT_INS.iter().find(|x| x.0 == path) {
15,416✔
2781
                                // self.built_ins.push(PathBuf::from(lib.0));
2782

2783
                                require_object.path = Some(PathOrBuiltIn::BuiltIn(lib.0.into()));
×
2784
                                require_object.for_syntax = true;
×
2785

2786
                                return Ok(());
×
2787
                                // continue;
2788
                            } else if self.custom_builtins.contains_key(path) {
27✔
2789
                                require_object.path =
×
2790
                                    Some(PathOrBuiltIn::BuiltIn(Cow::Owned(path.to_string())));
×
2791
                                require_object.for_syntax = true;
×
2792

2793
                                return Ok(());
×
2794
                            } else if self
9✔
2795
                                .module_resolvers
9✔
2796
                                .iter()
2797
                                .find(|x| x.exists(path))
×
2798
                                .is_some()
2799
                            {
2800
                                require_object.path =
×
2801
                                    Some(PathOrBuiltIn::BuiltIn(Cow::Owned(path.to_string())));
×
2802
                                require_object.for_syntax = true;
×
2803
                            } else {
2804
                                let mut current = self.name.clone();
9✔
2805
                                if current.is_file() {
9✔
2806
                                    current.pop();
9✔
2807
                                }
2808
                                current.push(PathBuf::from(path));
×
2809

2810
                                if !current.exists() {
×
2811
                                    if let Some(mut home) = home.clone() {
10✔
2812
                                        home.push(PathBuf::from(path));
×
2813
                                        current = home;
×
2814

2815
                                        if !current.exists() {
×
2816
                                            for dir in self.search_dirs {
×
2817
                                                let mut dir = dir.clone();
×
2818
                                                dir.push(path);
×
2819

2820
                                                if dir.exists() {
×
2821
                                                    current = dir;
×
2822
                                                    break;
×
2823
                                                }
2824
                                            }
2825
                                        }
2826

2827
                                        log::info!("Searching STEEL_HOME for {:?}", current);
×
2828
                                    } else {
2829
                                        for dir in self.search_dirs {
×
2830
                                            let mut dir = dir.clone();
×
2831
                                            dir.push(path);
×
2832

2833
                                            if dir.exists() {
×
2834
                                                current = dir;
×
2835
                                                break;
×
2836
                                            }
2837
                                        }
2838

2839
                                        stop!(Generic => format!("Module not found: {:?}", current); mod_name.span())
×
2840
                                    }
2841
                                }
2842

2843
                                require_object.for_syntax = true;
9✔
2844
                                let current = try_canonicalize(current);
×
2845
                                require_object.path = Some(PathOrBuiltIn::Path(current));
×
2846
                            }
2847
                        } else {
2848
                            stop!(BadSyntax => "for-syntax expects a string literal referring to a file or module"; mod_name.span());
×
2849
                        }
2850
                    }
2851
                    _ => {
×
2852
                        stop!(BadSyntax => "require accepts either a string literal, a for-syntax expression or an only-in expression"; l.location)
×
2853
                    }
2854
                }
2855
            }
2856

2857
            unknown => {
×
2858
                stop!(Generic => format!("require object expected a string literal referring to a file/module, found: {}", unknown); atom.span())
×
2859
            }
2860
        }
2861

2862
        Ok(())
125✔
2863
    }
2864

2865
    fn collect_requires(&mut self) -> Result<()> {
13,101✔
2866
        // unimplemented!()
2867

2868
        let mut exprs_without_requires = Vec::new();
26,202✔
2869
        let exprs = std::mem::take(&mut self.source_ast);
39,303✔
2870

2871
        let home = STEEL_HOME
26,202✔
2872
            .clone()
2873
            .map(|x| {
26,202✔
2874
                // TODO: Fix this - try to hack in a root drive
2875
                // for windows if a unix path is provided
2876
                if cfg!(target_os = "windows") {
13,101✔
2877
                    let mut result = x.trim_start_matches("/").to_string();
×
2878

2879
                    let mut iter = result.chars();
×
2880
                    iter.next();
×
2881
                    if matches!(iter.next(), Some(':')) {
×
2882
                        return PathBuf::from(result);
×
2883
                    }
2884

2885
                    result.insert(1, ':');
×
2886
                    return PathBuf::from(result);
×
2887
                }
2888

2889
                PathBuf::from(x)
26,202✔
2890
            })
2891
            .map(|mut x| {
26,202✔
2892
                x.push("cogs");
26,202✔
2893

2894
                x
13,101✔
2895
            });
2896

2897
        fn walk(
13,101✔
2898
            module_builder: &mut ModuleBuilder,
2899
            home: &Option<PathBuf>,
2900
            exprs_without_requires: &mut Vec<ExprKind>,
2901
            exprs: Vec<ExprKind>,
2902
        ) -> Result<()> {
2903
            for expr in exprs {
170,429✔
2904
                match expr {
78,664✔
2905
                    // Include require/for-syntax here
2906
                    // This way we have some understanding of what dependencies a file has
2907
                    ExprKind::Require(r) => {
3,637✔
2908
                        for atom in &r.modules {
11,691✔
2909
                            // TODO: Consider making this not a reference for r
2910
                            let require_object =
4,027✔
2911
                                module_builder.parse_require_object(home, &r, atom)?;
20,135✔
2912

2913
                            module_builder.require_objects.push(require_object);
×
2914
                        }
2915
                    }
2916
                    ExprKind::Begin(b) => {
×
2917
                        walk(module_builder, home, exprs_without_requires, b.exprs)?
×
2918
                    }
2919
                    _ => exprs_without_requires.push(expr),
225,081✔
2920
                }
2921
            }
2922

2923
            Ok(())
13,101✔
2924
        }
2925

2926
        walk(self, &home, &mut exprs_without_requires, exprs)?;
65,505✔
2927

2928
        self.source_ast = exprs_without_requires;
13,101✔
2929
        Ok(())
×
2930
    }
2931

2932
    #[allow(clippy::too_many_arguments)]
2933
    fn new_built_in(
1,515✔
2934
        name: PathBuf,
2935
        input: Cow<'static, str>,
2936
        compiled_modules: &'a mut crate::HashMap<PathBuf, CompiledModule>,
2937
        visited: &'a mut FxHashSet<PathBuf>,
2938
        file_metadata: &'a mut crate::HashMap<PathBuf, SystemTime>,
2939
        sources: &'a mut Sources,
2940
        kernel: &'a mut Option<Kernel>,
2941
        builtin_modules: ModuleContainer,
2942
        global_macro_map: &'a FxHashMap<InternedString, SteelMacro>,
2943
        custom_builtins: &'a HashMap<String, String>,
2944
        module_resolvers: &'a [Arc<dyn SourceModuleResolver>],
2945
    ) -> Result<Self> {
2946
        ModuleBuilder::raw(
2947
            name,
1,515✔
2948
            compiled_modules,
1,515✔
2949
            visited,
1,515✔
2950
            file_metadata,
1,515✔
2951
            sources,
1,515✔
2952
            kernel,
1,515✔
2953
            builtin_modules,
1,515✔
2954
            global_macro_map,
1,515✔
2955
            custom_builtins,
1,515✔
2956
            &[],
1,515✔
2957
            module_resolvers,
1,515✔
2958
            false,
2959
        )
2960
        .parse_builtin(input)
3,030✔
2961
    }
2962

2963
    fn new_from_path(
97✔
2964
        name: PathBuf,
2965
        compiled_modules: &'a mut crate::HashMap<PathBuf, CompiledModule>,
2966
        visited: &'a mut FxHashSet<PathBuf>,
2967
        file_metadata: &'a mut crate::HashMap<PathBuf, SystemTime>,
2968
        sources: &'a mut Sources,
2969
        kernel: &'a mut Option<Kernel>,
2970
        builtin_modules: ModuleContainer,
2971
        global_macro_map: &'a FxHashMap<InternedString, SteelMacro>,
2972
        custom_builtins: &'a HashMap<String, String>,
2973
        search_dirs: &'a [PathBuf],
2974
        module_resolvers: &'a [Arc<dyn SourceModuleResolver>],
2975
    ) -> Result<Self> {
2976
        ModuleBuilder::raw(
2977
            name,
97✔
2978
            compiled_modules,
97✔
2979
            visited,
97✔
2980
            file_metadata,
97✔
2981
            sources,
97✔
2982
            kernel,
97✔
2983
            builtin_modules,
97✔
2984
            global_macro_map,
97✔
2985
            custom_builtins,
97✔
2986
            search_dirs,
97✔
2987
            module_resolvers,
97✔
2988
            true,
2989
        )
2990
        .parse_from_path()
2991
    }
2992

2993
    fn raw(
1,612✔
2994
        name: PathBuf,
2995
        compiled_modules: &'a mut crate::HashMap<PathBuf, CompiledModule>,
2996
        visited: &'a mut FxHashSet<PathBuf>,
2997
        file_metadata: &'a mut crate::HashMap<PathBuf, SystemTime>,
2998
        sources: &'a mut Sources,
2999
        kernel: &'a mut Option<Kernel>,
3000
        builtin_modules: ModuleContainer,
3001
        global_macro_map: &'a FxHashMap<InternedString, SteelMacro>,
3002
        custom_builtins: &'a HashMap<String, String>,
3003
        search_dirs: &'a [PathBuf],
3004
        module_resolvers: &'a [Arc<dyn SourceModuleResolver>],
3005
        canonicalize: bool,
3006
    ) -> Self {
3007
        // println!("New module found: {:?}", name);
3008

3009
        let name = if canonicalize {
3,224✔
3010
            try_canonicalize(name)
194✔
3011
        } else {
3012
            name
1,515✔
3013
        };
3014

3015
        ModuleBuilder {
3016
            name,
3017
            main: false,
3018
            local_macros: FxHashSet::default(),
3,224✔
3019
            source_ast: Vec::new(),
3,224✔
3020
            // TODO: This used to be empty
3021
            macro_map: default_prelude_macros(),
3,224✔
3022
            require_objects: Vec::new(),
3,224✔
3023
            provides: Vec::new(),
3,224✔
3024
            provides_for_syntax: Vec::new(),
3,224✔
3025
            compiled_modules,
3026
            visited,
3027
            file_metadata,
3028
            sources,
3029
            kernel,
3030
            builtin_modules,
3031
            global_macro_map,
3032
            custom_builtins,
3033
            search_dirs,
3034
            module_resolvers,
3035
        }
3036
    }
3037

3038
    fn parse_builtin(mut self, input: Cow<'static, str>) -> Result<Self> {
1,515✔
3039
        #[cfg(feature = "profiling")]
3040
        let now = std::time::Instant::now();
×
3041

3042
        let id = self
3,030✔
3043
            .sources
1,515✔
3044
            .add_source(input.clone(), Some(self.name.clone()));
6,060✔
3045

3046
        let parsed = Parser::new_from_source(&input, self.name.clone(), Some(id))
9,090✔
3047
            .without_lowering()
3048
            .map(|x| x.and_then(lower_macro_and_require_definitions))
96,487✔
3049
            .collect::<std::result::Result<Vec<_>, ParseError>>()?;
3050

3051
        self.source_ast = parsed;
×
3052

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

3056
        // self.source_ast.pretty_print();
3057

3058
        Ok(self)
×
3059
    }
3060

3061
    fn parse_from_path(mut self) -> Result<Self> {
97✔
3062
        log::info!("Opening: {:?}", self.name);
97✔
3063
        let mut exprs = String::new();
194✔
3064

3065
        // If we were unable to resolve it via any of the built in module resolvers,
3066
        // then we check the file system.
3067
        let mut file = std::fs::File::open(&self.name).map_err(|err| {
388✔
3068
            let mut err = crate::SteelErr::from(err);
×
3069
            err.prepend_message(&format!("Attempting to load module from: {:?}", self.name));
×
3070
            err
×
3071
        })?;
3072
        self.file_metadata
×
3073
            .insert(self.name.clone(), file.metadata()?.modified()?);
194✔
3074

3075
        file.read_to_string(&mut exprs)?;
×
3076

3077
        let mut expressions = Parser::new(PRELUDE_STRING, SourceId::none())
194✔
3078
            .without_lowering()
3079
            .map(|x| x.and_then(lower_macro_and_require_definitions))
1,940✔
3080
            .collect::<std::result::Result<Vec<_>, ParseError>>()?;
3081

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

3084
        {
3085
            // Fetch the exprs after adding them to the sources
3086
            // We did _just_ add it, so its fine to unwrap
3087
            let guard = &self.sources.sources;
×
3088

3089
            let exprs = guard.get(id).unwrap();
×
3090

3091
            let mut parsed = Parser::new_from_source(exprs, self.name.clone(), Some(id))
97✔
3092
                .without_lowering()
3093
                .map(|x| x.and_then(lower_macro_and_require_definitions))
4,182✔
3094
                .collect::<std::result::Result<Vec<_>, ParseError>>()?;
3095

3096
            expressions.append(&mut parsed);
×
3097

3098
            self.source_ast = expressions;
×
3099
        }
3100

3101
        Ok(self)
×
3102
    }
3103
}
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