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

mattwparas / steel / 11924200389

20 Nov 2024 12:20AM UTC coverage: 46.725% (-0.2%) from 46.956%
11924200389

Pull #290

github

web-flow
Merge faebbd075 into a6f7286de
Pull Request #290: Fix provides within macros

37 of 263 new or added lines in 7 files covered. (14.07%)

27 existing lines in 11 files now uncovered.

12469 of 26686 relevant lines covered (46.72%)

483326.6 hits per line

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

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

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

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

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

37
use std::time::SystemTime;
38

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

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

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

56
        let e = $e;
57

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

61
        e
62
    }};
63
}
64

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

73
pub(crate) const MANGLER_SEPARATOR: &str = "__%#__";
74

75
macro_rules! create_prelude {
76
    (
77
        $( $module:literal, )*
78
        $( for_syntax $module_for_syntax:literal ),*
79
    ) => {
80

81
        pub static PRELUDE_WITHOUT_BASE: &str = concat!(
82
            $( "(require \"", $module, "\")\n", )*
83
            $( "(require (for-syntax \"", $module_for_syntax, "\"))\n", )*
84
        );
85

86
        pub static PRELUDE_STRING: &str = concat!(
87
            "(require-builtin steel/base)\n",
88
            $( "(require \"", $module, "\")\n", )*
89
            $( "(require (for-syntax \"", $module_for_syntax, "\"))\n", )*
90
        );
91
    }
92
}
93

94
declare_builtins!(
95
    "steel/option" => "../scheme/modules/option.scm",
96
    "steel/result" => "../scheme/modules/result.scm",
97
    "steel/iterators" => "../scheme/modules/iterators.scm",
98
    "steel/mutable-vectors" => "../scheme/modules/mvector.scm",
99
    "steel/async" => "../scheme/modules/async.scm",
100
    "steel/sync" => "../scheme/modules/sync.scm",
101
    "#%private/steel/contract" => "../scheme/modules/contracts.scm",
102
    "#%private/steel/print" => "../scheme/print.scm",
103
    "#%private/steel/control" => "../scheme/modules/parameters.scm",
104
    "#%private/steel/reader" => "../scheme/modules/reader.scm",
105
    "#%private/steel/stdlib" => "../scheme/stdlib.scm",
106
    "#%private/steel/match" => "../scheme/modules/match.scm"
107
);
108

109
create_prelude!(
110
    "#%private/steel/control",
111
    "#%private/steel/contract",
112
    "#%private/steel/print",
113
    "#%private/steel/reader",
114
    "#%private/steel/match",
115
    for_syntax "#%private/steel/control",
116
    for_syntax "#%private/steel/contract"
117
);
118

119
#[cfg(not(target_arch = "wasm32"))]
120
pub static STEEL_HOME: Lazy<Option<String>> = Lazy::new(|| {
4✔
121
    std::env::var("STEEL_HOME").ok().or_else(|| {
4✔
122
        let home = home::home_dir();
×
123

124
        home.map(|mut x: PathBuf| {
×
125
            x.push(".steel");
×
126

127
            // Just go ahead and initialize the directory, even though
128
            // this is probably not the best place to do this. This almost
129
            // assuredly could be lifted out of this check since failing here
130
            // could cause some annoyance.
131
            if !x.exists() {
×
132
                if let Err(_) = std::fs::create_dir(&x) {
×
133
                    eprintln!("Unable to create steel home directory {:?}", x)
×
134
                }
135
            }
136

137
            x.into_os_string().into_string().unwrap()
×
138
        })
139
    })
140
});
141

142
#[cfg(target_arch = "wasm32")]
143
pub static STEEL_HOME: Lazy<Option<String>> = Lazy::new(|| None);
144

145
pub fn steel_home() -> Option<String> {
8✔
146
    STEEL_HOME.clone()
8✔
147
}
148

149
/// Manages the modules
150
/// keeps some visited state on the manager for traversal
151
/// Also keeps track of the metadata for each file in order to determine
152
/// if it needs to be recompiled
153
#[derive(Clone, serde::Serialize, serde::Deserialize)]
154
pub(crate) struct ModuleManager {
155
    compiled_modules: FxHashMap<PathBuf, CompiledModule>,
156
    file_metadata: FxHashMap<PathBuf, SystemTime>,
157
    visited: FxHashSet<PathBuf>,
158
    custom_builtins: HashMap<String, String>,
159
}
160

161
impl ModuleManager {
162
    pub(crate) fn new(
5✔
163
        compiled_modules: FxHashMap<PathBuf, CompiledModule>,
164
        file_metadata: FxHashMap<PathBuf, SystemTime>,
165
    ) -> Self {
166
        ModuleManager {
167
            compiled_modules,
168
            file_metadata,
169
            visited: FxHashSet::default(),
5✔
170
            custom_builtins: HashMap::new(),
5✔
171
        }
172
    }
173

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

179
        self.custom_builtins.insert(module_name, text);
×
180
    }
181

182
    pub fn modules(&self) -> &FxHashMap<PathBuf, CompiledModule> {
38,078✔
183
        &self.compiled_modules
38,078✔
184
    }
185

186
    pub fn modules_mut(&mut self) -> &mut FxHashMap<PathBuf, CompiledModule> {
439✔
187
        &mut self.compiled_modules
439✔
188
    }
189

190
    pub(crate) fn default() -> Self {
5✔
191
        Self::new(FxHashMap::default(), FxHashMap::default())
5✔
192
    }
193

194
    // Add the module directly to the compiled module cache
195
    pub(crate) fn add_module(
×
196
        &mut self,
197
        path: PathBuf,
198
        global_macro_map: &mut FxHashMap<InternedString, SteelMacro>,
199
        kernel: &mut Option<Kernel>,
200
        sources: &mut Sources,
201
        builtin_modules: ModuleContainer,
202
    ) -> Result<()> {
203
        // todo!()
204

205
        self.visited.clear();
×
206

207
        // TODO: Expand macros on the fly when visiting a module. Don't wait till the end
208
        // Macro expansion should happen as we enter a module.
209
        let mut module_builder = ModuleBuilder::new_from_path(
210
            path,
×
211
            &mut self.compiled_modules,
×
212
            &mut self.visited,
×
213
            &mut self.file_metadata,
×
214
            sources,
×
215
            kernel,
×
216
            builtin_modules,
×
217
            global_macro_map,
×
218
            &self.custom_builtins,
×
219
            &[],
×
220
        )?;
221

222
        module_builder.compile()?;
×
223

224
        // println!("{:#?}", self.compiled_modules);
225

226
        Ok(())
×
227
    }
228

229
    // #[allow(unused)]
230
    pub(crate) fn compile_main(
803✔
231
        &mut self,
232
        global_macro_map: &mut FxHashMap<InternedString, SteelMacro>,
233
        kernel: &mut Option<Kernel>,
234
        sources: &mut Sources,
235
        mut exprs: Vec<ExprKind>,
236
        path: Option<PathBuf>,
237
        builtin_modules: ModuleContainer,
238
        lifted_kernel_environments: &mut HashMap<String, KernelDefMacroSpec>,
239
        lifted_macro_environments: &mut HashSet<PathBuf>,
240
        search_dirs: &[PathBuf],
241
    ) -> Result<Vec<ExprKind>> {
242
        // Wipe the visited set on entry
243
        self.visited.clear();
803✔
244

245
        // TODO
246
        // This is also explicitly wrong -> we should separate the global macro map from the macros found locally in this module
247
        // For instance, (cond) is global, but (define-syntax blagh) might be local to main
248
        // if a module then defines a function (blagh) that is used inside its scope, this would expand the macro in that scope
249
        // which we do not want
250
        extract_macro_defs(&mut exprs, global_macro_map)?;
811✔
251

252
        let mut module_builder = ModuleBuilder::main(
253
            path,
795✔
254
            exprs,
795✔
255
            &mut self.compiled_modules,
795✔
256
            &mut self.visited,
795✔
257
            &mut self.file_metadata,
795✔
258
            sources,
795✔
259
            kernel,
795✔
260
            builtin_modules,
795✔
261
            global_macro_map,
795✔
262
            &self.custom_builtins,
795✔
263
            search_dirs,
795✔
264
        )?;
265

266
        let mut module_statements = module_builder.compile()?;
795✔
267

NEW
268
        module_builder.collect_provides()?;
×
269

270
        let mut ast = module_builder.source_ast;
795✔
271

272
        let mut require_defines = Vec::new();
795✔
273

274
        let mut explicit_requires = HashMap::new();
795✔
275

276
        for require_object in &module_builder.require_objects {
3,159✔
277
            let path = require_object.path.get_path();
278
            explicit_requires.clear();
279

280
            // If there _are_ explicit identifiers to import, limit what we import to what
281
            // is in the set
282
            for ident in &require_object.idents_to_import {
1,200✔
283
                match ident {
284
                    MaybeRenamed::Normal(i) => {
7✔
285
                        explicit_requires.insert(i.atom_identifier().unwrap().clone(), None);
7✔
286
                    }
287

288
                    MaybeRenamed::Renamed(from, to) => {
2✔
289
                        explicit_requires.insert(
2✔
290
                            from.atom_identifier().unwrap().clone(),
2✔
291
                            Some(to.atom_identifier().unwrap().clone()),
2✔
292
                        );
293
                    }
294
                }
295
            }
296

297
            // println!("{:?}", path);
298
            let module = if let Some(module) = module_builder.compiled_modules.get(path.as_ref()) {
2,364✔
299
                module
300
            } else {
301
                // log::debug!(target: "modules", "No provides found for module, skipping: {:?}", path);
302

303
                continue;
×
304
            };
305

306
            for provide_expr in &module.provides {
6,142✔
307
                // For whatever reason, the value coming into module.provides is an expression like: (provide expr...)
308
                for provide in &provide_expr.list().unwrap().args[1..] {
14,282✔
309
                    let other_module_prefix = module.prefix();
14,282✔
310

311
                    // TODO: Expand the contract out into something we expect
312
                    // Otherwise, this is going to blow up
313
                    match provide {
14,282✔
314
                        ExprKind::List(l) => {
26✔
315
                            if let Some(qualifier) = l.first_ident() {
52✔
316
                                match *qualifier {
317
                                    x if x == *REQUIRE_IDENT_SPEC => {
52✔
318
                                        // Directly expand into define/contract, but with the value just being the hash get below
319

320
                                        // (bind/c contract name 'name)
321

322
                                        let name = l.args.get(1).unwrap();
26✔
323

324
                                        if !explicit_requires.is_empty()
26✔
325
                                            && !name
9✔
326
                                                .atom_identifier()
9✔
327
                                                .map(|x| explicit_requires.contains_key(x))
18✔
328
                                                .unwrap_or_default()
329
                                        {
330
                                            continue;
6✔
331
                                        }
332

333
                                        if module
20✔
334
                                            .macro_map
20✔
335
                                            .contains_key(name.atom_identifier().unwrap())
20✔
336
                                        {
337
                                            continue;
×
338
                                        }
339

340
                                        let hash_get = expr_list![
20✔
341
                                            ExprKind::atom(*PROTO_HASH_GET),
20✔
342
                                            ExprKind::atom(
20✔
343
                                                CompactString::new(MODULE_PREFIX)
20✔
344
                                                    + &other_module_prefix
20✔
345
                                            ),
346
                                            ExprKind::Quote(Box::new(Quote::new(
20✔
347
                                                name.clone(),
20✔
348
                                                SyntaxObject::default(TokenType::Quote)
20✔
349
                                            ))),
350
                                        ];
351

352
                                        let mut owned_name = name.clone();
20✔
353

354
                                        // If we have the alias listed, we should use it
355
                                        if !explicit_requires.is_empty() {
20✔
356
                                            if let Some(alias) = explicit_requires
3✔
357
                                                .get(name.atom_identifier().unwrap())
3✔
358
                                                .copied()
359
                                                .flatten()
360
                                            {
361
                                                *owned_name.atom_identifier_mut().unwrap() =
362
                                                    alias.clone();
363
                                            }
364
                                        }
365

366
                                        if let Some(prefix) = &require_object.prefix {
3✔
367
                                            if let Some(existing) = owned_name.atom_identifier_mut()
3✔
368
                                            {
369
                                                let mut prefixed_identifier = prefix.clone();
3✔
370
                                                prefixed_identifier.push_str(existing.resolve());
3✔
371

372
                                                // Update the existing identifier to point to a new one with the prefix applied
373
                                                *existing = prefixed_identifier.into();
3✔
374
                                            }
375
                                        }
376

377
                                        let define = ExprKind::Define(Box::new(Define::new(
20✔
378
                                            owned_name,
20✔
379
                                            hash_get,
20✔
380
                                            SyntaxObject::default(TokenType::Define),
20✔
381
                                        )));
382

383
                                        require_defines.push(define);
20✔
384
                                    }
385
                                    _ => {
386
                                        stop!(TypeMismatch => format!("provide expects either an identifier, (for-syntax <ident>), or (contract/out ...), found: {}", provide))
×
387
                                    }
388
                                }
389
                            } else {
390
                                stop!(TypeMismatch => "provide expects either an identifier or a (for-syntax <ident>)")
×
391
                            }
392
                        }
393
                        ExprKind::Atom(_) => {
394
                            if !explicit_requires.is_empty()
14,256✔
395
                                && !provide
27✔
396
                                    .atom_identifier()
27✔
397
                                    .map(|x| explicit_requires.contains_key(x))
54✔
398
                                    .unwrap_or_default()
399
                            {
400
                                continue;
21✔
401
                            }
402

403
                            if module
14,235✔
404
                                .macro_map
14,235✔
405
                                .contains_key(provide.atom_identifier().unwrap())
14,235✔
406
                            {
407
                                continue;
488✔
408
                            }
409

410
                            let hash_get = expr_list![
13,747✔
411
                                ExprKind::atom(*PROTO_HASH_GET),
13,747✔
412
                                ExprKind::atom(
13,747✔
413
                                    CompactString::new(MODULE_PREFIX) + &other_module_prefix
13,747✔
414
                                ),
415
                                ExprKind::Quote(Box::new(Quote::new(
13,747✔
416
                                    provide.clone(),
13,747✔
417
                                    SyntaxObject::default(TokenType::Quote)
13,747✔
418
                                ))),
419
                            ];
420

421
                            let mut owned_provide = provide.clone();
13,747✔
422

423
                            // If we have the alias listed, we should use it
424
                            if !explicit_requires.is_empty() {
13,747✔
425
                                if let Some(alias) = explicit_requires
8✔
426
                                    .get(provide.atom_identifier().unwrap())
6✔
427
                                    .copied()
428
                                    .flatten()
429
                                {
430
                                    *owned_provide.atom_identifier_mut().unwrap() = alias.clone();
431
                                }
432
                            }
433

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

438
                                if let Some(existing) = owned_provide.atom_identifier_mut() {
18✔
439
                                    let mut prefixed_identifier = prefix.clone();
9✔
440
                                    prefixed_identifier.push_str(existing.resolve());
9✔
441

442
                                    // Update the existing identifier to point to a new one with the prefix applied
443
                                    *existing = prefixed_identifier.into();
9✔
444
                                }
445
                            }
446

447
                            let define = ExprKind::Define(Box::new(Define::new(
13,747✔
448
                                owned_provide,
13,747✔
449
                                hash_get,
13,747✔
450
                                SyntaxObject::default(TokenType::Define),
13,747✔
451
                            )));
452

453
                            // println!("{}", define);
454

455
                            require_defines.push(define);
13,747✔
456
                        }
457
                        _ => {
458
                            stop!(TypeMismatch => "provide expression needs to either be a `contract/out` form or an identifier")
×
459
                        }
460
                    }
461
                }
462
            }
463
        }
464

465
        let mut mangled_asts = Vec::with_capacity(ast.len());
795✔
466

467
        // TODO: Move this to the lower level as well
468
        // It seems we're only doing this expansion at the top level, but we _should_ do this at the lower level as well
469
        for require_object in module_builder.require_objects.iter()
1,977✔
470
        // .filter(|x| x.for_syntax)
471
        // .map(|x| x.path.get_path())
472
        {
473
            let require_for_syntax = require_object.path.get_path();
1,182✔
474

475
            let (module, in_scope_macros, mut name_mangler) = Self::find_in_scope_macros(
476
                &mut self.compiled_modules,
1,182✔
477
                require_for_syntax.as_ref(),
1,182✔
478
                &require_object,
1,182✔
479
                &mut mangled_asts,
1,182✔
480
            );
481

482
            // let kernel_macros_in_scope: HashSet<_> =
483
            //     module.provides_for_syntax.iter().cloned().collect();
484

485
            // let defmacros_exported: HashSet<_> = module.
486

487
            // dbg!(&kernel_macros_in_scope);
488

489
            let module_name = module.name.to_str().unwrap().to_string();
1,182✔
490

491
            if let Some(kernel) = kernel.as_mut() {
2,364✔
492
                if kernel.exported_defmacros(&module_name).is_some() {
162✔
493
                    lifted_kernel_environments.insert(
162✔
494
                        module_name.clone(),
162✔
495
                        KernelDefMacroSpec {
162✔
496
                            _env: module_name.clone(),
162✔
497
                            _exported: None,
162✔
498
                            name_mangler: name_mangler.clone(),
162✔
499
                        },
500
                    );
501
                }
502
            }
503

504
            // TODO: This isn't right - only check if there are defmacro things
505
            // that we need to lift - just check the values that are in the defmacros
506
            // environment in the kernel
507
            // if !kernel_macros_in_scope.is_empty() {
508
            //     lifted_kernel_environments.insert(
509
            //         module_name.clone(),
510
            //         KernelDefMacroSpec {
511
            //             env: module_name,
512
            //             exported: None,
513
            //             name_mangler: name_mangler.clone(),
514
            //         },
515
            //     );
516
            // }
517

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

520
            for expr in ast.iter_mut() {
2,898✔
521
                // @matt 12/8/2023
522
                // The easiest thing to do here, is to go to the other module, and find
523
                // what defmacros have been exposed on the require for syntax. Once those
524
                // have been found, we run a pass with kernel expansion, limiting the
525
                // expander to only use the macros that we've exposed. After that,
526
                // we run the expansion again, using the full suite of defmacro capabilities.
527
                //
528
                // The question that remains - how to define the neat phases of what kinds
529
                // of macros can expand into what? Can defmacro -> syntax-rules -> defmacro?
530
                // This could eventually prove to be cumbersome, but it is still early
531
                // for defmacro. Plus, I need to create a syntax-case or syntax-parse
532
                // frontend before the defmacro style macros become too pervasive.
533
                //
534
                // TODO: Replicate this behavior over to builtin modules
535

536
                // First expand the in scope macros
537
                // These are macros
538
                let mut expander = Expander::new(&in_scope_macros);
1,716✔
539
                expander.expand(expr)?;
1,716✔
540
                let changed = false;
1,716✔
541

542
                // (first_round_expanded, changed) = expand_kernel_in_env_with_allowed(
543
                //     first_round_expanded,
544
                //     kernel.as_mut(),
545
                //     // We don't need to expand those here
546
                //     ModuleContainer::default(),
547
                //     module.name.to_str().unwrap().to_string(),
548
                //     &kernel_macros_in_scope,
549
                // )?;
550

551
                // If the kernel expander expanded into something - go ahead
552
                // and expand all of the macros in this
553
                // if changed || expander.changed {
554
                // Expand here?
555
                // first_round_expanded = expand(first_round_expanded, &module.macro_map)?;
556

557
                // Probably don't need this
558
                // (first_round_expanded, changed) = expand_kernel_in_env_with_change(
559
                //     first_round_expanded,
560
                //     kernel.as_mut(),
561
                //     ModuleContainer::default(),
562
                //     module.name.to_str().unwrap().to_string(),
563
                // )?;
564

565
                // This is pretty suspect, and needs to be revisited - only the output of the
566
                // macro expansion and not the whole thing needs to be mangled most likely.
567
                // Otherwise, we'll run into weird stuff?
568
                // if changed {
569
                //     name_mangler.visit(&mut first_round_expanded);
570
                // }
571
                // }
572

573
                if expander.changed || changed {
3,044✔
574
                    let _source_id = sources.get_source_id(&module.name).unwrap();
388✔
575

576
                    // let mut fully_expanded = first_round_expanded;
577

578
                    expand(
579
                        expr,
388✔
580
                        &module.macro_map,
388✔
581
                        // source_id,
582
                    )?;
583

584
                    // Expanding the kernel with only these macros...
585
                    let changed = expand_kernel_in_env_with_change(
586
                        expr,
388✔
587
                        kernel.as_mut(),
388✔
588
                        // We don't need to expand those here
589
                        ModuleContainer::default(),
388✔
590
                        &module_name,
388✔
591
                        // &kernel_macros_in_scope,
592
                    )?;
593

594
                    if changed {
×
595
                        name_mangler.visit(expr);
×
596
                    }
597

598
                    // lifted_kernel_environments.insert(
599
                    //     module_name.clone(),
600
                    //     KernelDefMacroSpec {
601
                    //         env: module_name,
602
                    //         exported: None,
603
                    //         name_mangler: name_mangler.clone(),
604
                    //     },
605
                    // );
606

607
                    // Ok(fully_expanded)
608
                }
609
                // else {
610
                //     Ok(first_round_expanded)
611
                // }
612
            }
613

614
            // ast = ast
615
            //     .into_iter()
616
            //     .map(|x| {
617
            //         // @matt 12/8/2023
618
            //         // The easiest thing to do here, is to go to the other module, and find
619
            //         // what defmacros have been exposed on the require for syntax. Once those
620
            //         // have been found, we run a pass with kernel expansion, limiting the
621
            //         // expander to only use the macros that we've exposed. After that,
622
            //         // we run the expansion again, using the full suite of defmacro capabilities.
623
            //         //
624
            //         // The question that remains - how to define the neat phases of what kinds
625
            //         // of macros can expand into what? Can defmacro -> syntax-rules -> defmacro?
626
            //         // This could eventually prove to be cumbersome, but it is still early
627
            //         // for defmacro. Plus, I need to create a syntax-case or syntax-parse
628
            //         // frontend before the defmacro style macros become too pervasive.
629
            //         //
630
            //         // TODO: Replicate this behavior over to builtin modules
631

632
            //         // First expand the in scope macros
633
            //         // These are macros
634
            //         let mut expander = Expander::new(&in_scope_macros);
635
            //         let mut first_round_expanded = expander.expand(x)?;
636
            //         let mut changed = false;
637

638
            //         // (first_round_expanded, changed) = expand_kernel_in_env_with_allowed(
639
            //         //     first_round_expanded,
640
            //         //     kernel.as_mut(),
641
            //         //     // We don't need to expand those here
642
            //         //     ModuleContainer::default(),
643
            //         //     module.name.to_str().unwrap().to_string(),
644
            //         //     &kernel_macros_in_scope,
645
            //         // )?;
646

647
            //         // If the kernel expander expanded into something - go ahead
648
            //         // and expand all of the macros in this
649
            //         // if changed || expander.changed {
650
            //         // Expand here?
651
            //         // first_round_expanded = expand(first_round_expanded, &module.macro_map)?;
652

653
            //         // Probably don't need this
654
            //         // (first_round_expanded, changed) = expand_kernel_in_env_with_change(
655
            //         //     first_round_expanded,
656
            //         //     kernel.as_mut(),
657
            //         //     ModuleContainer::default(),
658
            //         //     module.name.to_str().unwrap().to_string(),
659
            //         // )?;
660

661
            //         // This is pretty suspect, and needs to be revisited - only the output of the
662
            //         // macro expansion and not the whole thing needs to be mangled most likely.
663
            //         // Otherwise, we'll run into weird stuff?
664
            //         // if changed {
665
            //         //     name_mangler.visit(&mut first_round_expanded);
666
            //         // }
667
            //         // }
668

669
            //         if expander.changed || changed {
670
            //             let source_id = sources.get_source_id(&module.name).unwrap();
671

672
            //             let mut fully_expanded = first_round_expanded;
673

674
            //             expand(
675
            //                 &mut fully_expanded,
676
            //                 &module.macro_map,
677
            //                 // source_id,
678
            //             )?;
679

680
            //             let module_name = module.name.to_str().unwrap().to_string();
681

682
            //             // Expanding the kernel with only these macros...
683
            //             let changed = expand_kernel_in_env_with_change(
684
            //                 &mut fully_expanded,
685
            //                 kernel.as_mut(),
686
            //                 // We don't need to expand those here
687
            //                 ModuleContainer::default(),
688
            //                 module_name.clone(),
689
            //                 // &kernel_macros_in_scope,
690
            //             )?;
691

692
            //             if changed {
693
            //                 name_mangler.visit(&mut fully_expanded);
694
            //             }
695

696
            //             // lifted_kernel_environments.insert(
697
            //             //     module_name.clone(),
698
            //             //     KernelDefMacroSpec {
699
            //             //         env: module_name,
700
            //             //         exported: None,
701
            //             //         name_mangler: name_mangler.clone(),
702
            //             //     },
703
            //             // );
704

705
            //             Ok(fully_expanded)
706
            //         } else {
707
            //             Ok(first_round_expanded)
708
            //         }
709
            //     })
710
            //     .collect::<Result<_>>()?;
711

712
            // Global macro map - also need to expand with ALL macros
713
            // post expansion in the target environment, which means we can't _just_
714
            // extend the global macro map with the target in scope macros, we need to
715
            // do something like the two pass expansion
716
            global_macro_map.extend(in_scope_macros);
1,182✔
717

718
            lifted_macro_environments.insert(module.name.clone());
1,182✔
719
        }
720

721
        // Include the defines from the modules now imported
722
        module_statements.append(&mut require_defines);
795✔
723

724
        // The next two lines here expand _all_ of the source code with the top level macros
725
        // This is necessary because of the std library macros, although this should be able to be
726
        // solved with scoped imports of the standard library explicitly
727
        module_statements.append(&mut ast);
795✔
728

729
        time!("Top level macro evaluation time", {
790✔
730
            for expr in module_statements.iter_mut() {
22,541✔
731
                expand(expr, global_macro_map)?;
21,751✔
732
            }
733
        });
734

735
        // @Matt 7/4/23
736
        // TODO: With mangling, this could cause problems. We'll want to un-mangle quotes AFTER the macro has been expanded,
737
        // in order to preserve the existing behavior.
738
        // let result = module_statements
739
        //     .into_iter()
740
        //     .map(|x| expand(x, global_macro_map))
741
        //     .collect::<Result<_>>();
742

743
        // result
744

745
        Ok(module_statements)
790✔
746
    }
747

748
    fn find_in_scope_macros<'a>(
2,575✔
749
        compiled_modules: &'a mut FxHashMap<PathBuf, CompiledModule>,
750
        require_for_syntax: &'a PathBuf,
751
        require_object: &'a RequireObject,
752
        mangled_asts: &'a mut Vec<ExprKind>,
753
    ) -> (
754
        &'a CompiledModule,
755
        FxHashMap<InternedString, SteelMacro>,
756
        NameMangler,
757
    ) {
758
        let module = compiled_modules
2,575✔
759
            .get_mut(require_for_syntax)
2,575✔
760
            .expect(&format!("Module missing!: {:?}", require_for_syntax));
2,575✔
761

762
        let prefix = module.prefix();
2,575✔
763

764
        let globals = collect_globals(&module.ast);
2,575✔
765

766
        let mut name_mangler = NameMangler::new(globals, prefix);
2,575✔
767

768
        // If the module hasn't been emitted already, then include it here
769
        if !module.emitted {
2,575✔
770
            let mut module_ast = module.ast.clone();
×
771

772
            name_mangler.mangle_vars(&mut module_ast);
×
773

774
            mangled_asts.append(&mut module_ast);
×
775
        }
776

777
        // let provided_macros = module.provides_for
778
        // let expander = Expander::new(&module.macro_map);
779
        // TODO
780
        // expand expressions one by one
781
        // if expansion with _just_ public macros from the required module doesn't do anything, stop
782
        // if it _does_ change, do another pass with all of the macros in scope
783
        // do this for each of the expressions in the file in this loop
784
        // TODO -> try not cloning this
785
        // TODO -> do this in the module expansion as well
786
        let mut in_scope_macros = module
2,575✔
787
            .provides_for_syntax
2,575✔
788
            .iter()
789
            // Chain with just the normal provides!
790
            // .chain(module.provides)
791
            // .filter_map(|x| module.macro_map.get(x).map(|m| (*x, m.clone()))) // TODO -> fix this unwrap
792
            // .filter_map(|x| module.macro_map.get_mut(x).map(|m| (*x, m))) // TODO -> fix this unwrap
793
            .filter_map(|x| {
5,857✔
794
                let smacro = module.macro_map.get_mut(x);
3,282✔
795

796
                if let Some(smacro) = smacro {
6,546✔
797
                    if !smacro.special_mangled {
×
798
                        for expr in smacro.exprs_mut() {
14,806✔
799
                            name_mangler.visit(expr);
5,771✔
800
                        }
801
                    }
802

803
                    Some((*x, smacro.clone()))
3,264✔
804
                } else {
805
                    None
18✔
806
                }
807

808
                // if !x.1.special_mangled {
809
                //     for expr in x.1.exprs_mut() {
810
                //         name_mangler.visit(expr);
811
                //     }
812
                // }
813

814
                // (x.0, x.1.clone())
815
            })
816
            .collect::<FxHashMap<_, _>>();
817

818
        // If the require_object specifically imports things, we should reference it
819

820
        if !require_object.idents_to_import.is_empty() {
2,575✔
821
            for maybe in &require_object.idents_to_import {
28✔
822
                match maybe {
×
823
                    MaybeRenamed::Normal(n) => {
10✔
824
                        if let Some(ident) = n.atom_identifier() {
10✔
825
                            if let Some(mut m) = module.macro_map.get(ident).cloned() {
×
826
                                for expr in m.exprs_mut() {
×
827
                                    name_mangler.visit(expr);
×
828
                                }
829

830
                                if let Some(prefix) = &require_object.prefix {
×
831
                                    in_scope_macros
×
832
                                        .insert((prefix.to_string() + ident.resolve()).into(), m);
×
833
                                } else {
834
                                    in_scope_macros.insert(*ident, m);
×
835
                                }
836
                            }
837
                        }
838
                    }
839
                    MaybeRenamed::Renamed(from, to) => {
2✔
840
                        if let Some(ident) = from.atom_identifier() {
4✔
841
                            if let Some(mut m) = module.macro_map.get(ident).cloned() {
×
842
                                for expr in m.exprs_mut() {
×
843
                                    name_mangler.visit(expr);
×
844
                                }
845
                                // TODO: Remove this unwrap
846
                                // in_scope_macros.insert(*to.atom_identifier().unwrap(), m);
847

848
                                if let Some(prefix) = &require_object.prefix {
×
849
                                    in_scope_macros.insert(
×
850
                                        (prefix.to_string()
×
851
                                            + to.atom_identifier().unwrap().resolve())
×
852
                                        .into(),
×
853
                                        m,
×
854
                                    );
855
                                } else {
856
                                    in_scope_macros.insert(*to.atom_identifier().unwrap(), m);
×
857
                                }
858
                            }
859
                        }
860
                    }
861
                }
862
            }
863
        } else {
864
            // Pull in all of the macros that the module exposes
865

866
            for provide_expr in &module.provides {
14,413✔
867
                if let Some(provide_expr) = provide_expr.list() {
5,921✔
868
                    for ident in provide_expr.args.split_first().unwrap().1 {
36,289✔
869
                        // println!("Looking for {}", ident);
870

871
                        if let Some(ident) = ident.atom_identifier() {
72,066✔
872
                            if let Some(mut m) = module.macro_map.get(ident).cloned() {
650✔
873
                                // println!("Pulling in macro: {}", ident);
874

875
                                for expr in m.exprs_mut() {
2,166✔
876
                                    name_mangler.visit(expr);
1,083✔
877
                                }
878

879
                                if let Some(prefix) = &require_object.prefix {
650✔
880
                                    in_scope_macros
×
881
                                        .insert((prefix.to_string() + ident.resolve()).into(), m);
×
882
                                } else {
883
                                    in_scope_macros.insert(*ident, m);
650✔
884
                                }
885
                            }
886
                        }
887
                    }
888
                }
889
            }
890
        }
891

892
        // Check what macros are in scope here
893
        // println!(
894
        //     "In scope macros: {:#?}",
895
        //     in_scope_macros.keys().collect::<Vec<_>>()
896
        // );
897
        (module, in_scope_macros, name_mangler)
2,575✔
898
    }
899
}
900

901
// Pre-compile module to bytecode? Is it even possible?
902
// Dynamically linking the module would then make it relatively
903
// easy to just load everything up at the start.
904
// Compiled module _should_ be possible now. Just create a target
905
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
906
pub struct CompiledModule {
907
    name: PathBuf,
908
    provides: Vec<ExprKind>,
909
    require_objects: Vec<RequireObject>,
910
    provides_for_syntax: Vec<InternedString>,
911
    pub(crate) macro_map: FxHashMap<InternedString, SteelMacro>,
912
    ast: Vec<ExprKind>,
913
    emitted: bool,
914
    cached_prefix: CompactString,
915
}
916

917
pub static MANGLER_PREFIX: &'static str = "##mm";
918
pub static MODULE_PREFIX: &'static str = "__module-";
919
pub static MANGLED_MODULE_PREFIX: &'static str = "__module-##mm";
920

921
pub fn path_to_module_name(name: PathBuf) -> String {
×
922
    let mut base = CompactString::new(MANGLED_MODULE_PREFIX);
×
923

924
    if let Some(steel_home) = STEEL_HOME.as_ref() {
×
925
        // Intern this?
926
        let name = name
927
            .to_str()
928
            .unwrap()
929
            .trim_start_matches(steel_home.as_str());
930

931
        let interned = InternedString::from_str(&name);
932
        let id = interned.get().into_inner();
933

934
        base.push_str(&id.to_string());
935
        base.push_str(MANGLER_SEPARATOR);
936
    } else {
937
        let interned = InternedString::from_str(name.to_str().unwrap());
×
938
        let id = interned.get().into_inner();
×
939

940
        base.push_str(&id.to_string());
×
941
        base.push_str(MANGLER_SEPARATOR);
×
942
    }
943

944
    base.into_string()
×
945
}
946

947
// TODO: @Matt 6/12/23 - This _should_ be serializable. If possible, we can try to store intermediate objects down to some file.
948
impl CompiledModule {
949
    pub fn new(
1,027✔
950
        name: PathBuf,
951
        provides: Vec<ExprKind>,
952
        require_objects: Vec<RequireObject>,
953
        provides_for_syntax: Vec<InternedString>,
954
        macro_map: FxHashMap<InternedString, SteelMacro>,
955
        ast: Vec<ExprKind>,
956
    ) -> Self {
957
        let mut base = CompactString::new(MANGLER_PREFIX);
1,027✔
958

959
        if let Some(steel_home) = STEEL_HOME.as_ref() {
2,054✔
960
            // Intern this?
961
            let name = name
962
                .to_str()
963
                .unwrap()
964
                .trim_start_matches(steel_home.as_str());
965

966
            let interned = InternedString::from_str(&name);
967
            let id = interned.get().into_inner();
968

969
            // base.push_str(name);
970
            base.push_str(&id.to_string());
971
            base.push_str(MANGLER_SEPARATOR);
972

973
            // println!("{}", base);
974
            // println!("Byte length: {}", base.len());
975
        } else {
976
            let interned = InternedString::from_str(name.to_str().unwrap());
×
977
            let id = interned.get().into_inner();
×
978

979
            // base.push_str(self.name.to_str().unwrap());
980
            base.push_str(&id.to_string());
×
981
            base.push_str(MANGLER_SEPARATOR);
×
982
        }
983

984
        Self {
985
            name,
986
            provides,
987
            require_objects,
988
            provides_for_syntax,
989
            macro_map,
990
            ast,
991
            emitted: false,
992
            cached_prefix: base,
993
        }
994
    }
995

996
    // TODO: Should cache this
997
    pub fn prefix(&self) -> CompactString {
19,277✔
998
        self.cached_prefix.clone()
19,277✔
999
    }
1000

1001
    pub fn get_ast(&self) -> &[ExprKind] {
×
1002
        &self.ast
×
1003
    }
1004

1005
    pub fn get_provides(&self) -> &[ExprKind] {
×
1006
        &self.provides
×
1007
    }
1008

1009
    // pub fn get_requires(&self) -> &[PathBuf] {
1010
    //     &self.requires
1011
    // }
1012

1013
    pub fn set_emitted(&mut self, emitted: bool) {
1,027✔
1014
        self.emitted = emitted;
1,027✔
1015
    }
1016

1017
    fn to_top_level_module(
1,027✔
1018
        &self,
1019
        modules: &FxHashMap<PathBuf, CompiledModule>,
1020
        global_macro_map: &FxHashMap<InternedString, SteelMacro>,
1021
    ) -> Result<ExprKind> {
1022
        let mut globals = collect_globals(&self.ast);
1,027✔
1023

1024
        let mut exprs = self.ast.clone();
1,027✔
1025

1026
        let mut provide_definitions = Vec::new();
1,027✔
1027

1028
        // TODO: Change this to not use the full path. Unfortunately that isn't portable,
1029
        // so we should use something that is more agnostic of the target location
1030
        // if we were to load.
1031
        //
1032
        // Probably a better idea would be to somehow assign a unique ID to each module;
1033
        // the path from the $STEEL_HOME would be relatively safe. That way we can just strip
1034
        // the $STEEL_HOME root away from the path if it starts with it, and then have
1035
        // that resolve to be the "name" of the module.
1036
        let prefix = self.prefix();
1,027✔
1037

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

1042
        let mut explicit_requires = HashMap::new();
1,027✔
1043

1044
        // TODO: This is the same as the top level, they should be merged
1045
        for require_object in &self.require_objects {
3,813✔
1046
            let path = require_object.path.get_path();
1047

1048
            explicit_requires.clear();
1049

1050
            for ident in &require_object.idents_to_import {
1,399✔
1051
                match ident {
1052
                    MaybeRenamed::Normal(i) => {
3✔
1053
                        explicit_requires.insert(i.atom_identifier().unwrap().clone(), None);
3✔
1054
                    }
1055
                    MaybeRenamed::Renamed(from, to) => {
×
1056
                        explicit_requires.insert(
×
1057
                            from.atom_identifier().unwrap().clone(),
×
1058
                            Some(to.atom_identifier().unwrap().clone()),
×
1059
                        );
1060
                    }
1061
                }
1062
            }
1063

1064
            // println!("{:?}", path);
1065
            // println!("{:?}", modules.keys().collect::<Vec<_>>());
1066
            let module = modules.get(path.as_ref()).unwrap();
1,393✔
1067

1068
            let other_module_prefix = module.prefix();
1,393✔
1069

1070
            for provide_expr in &module.provides {
8,283✔
1071
                // For whatever reason, the value coming into module.provides is an expression like: (provide expr...)
1072
                for provide in &provide_expr.list().unwrap().args[1..] {
22,048✔
1073
                    match provide {
22,048✔
1074
                        ExprKind::List(l) => {
495✔
1075
                            if let Some(qualifier) = l.first_ident() {
990✔
1076
                                if module.macro_map.contains_key(qualifier) {
1077
                                    continue;
×
1078
                                }
1079

1080
                                match *qualifier {
495✔
1081
                                    x if x == *REQUIRE_IDENT_SPEC => {
990✔
1082
                                        // Directly expand into define/contract, but with the value just being the hash get below
1083

1084
                                        // (bind/c contract name 'name)
1085

1086
                                        let name = l.args.get(1).unwrap();
495✔
1087

1088
                                        if !explicit_requires.is_empty()
495✔
1089
                                            && !name
×
1090
                                                .atom_identifier()
×
1091
                                                .map(|x| explicit_requires.contains_key(x))
×
1092
                                                .unwrap_or_default()
1093
                                        {
1094
                                            continue;
×
1095
                                        }
1096

1097
                                        if module
495✔
1098
                                            .macro_map
495✔
1099
                                            .contains_key(name.atom_identifier().unwrap())
495✔
1100
                                        {
1101
                                            continue;
×
1102
                                        }
1103

1104
                                        let hash_get = expr_list![
495✔
1105
                                            ExprKind::atom(*PROTO_HASH_GET),
495✔
1106
                                            ExprKind::atom(
495✔
1107
                                                CompactString::new(MODULE_PREFIX)
495✔
1108
                                                    + &other_module_prefix
495✔
1109
                                            ),
1110
                                            ExprKind::Quote(Box::new(Quote::new(
495✔
1111
                                                name.clone(),
495✔
1112
                                                SyntaxObject::default(TokenType::Quote)
495✔
1113
                                            ))),
1114
                                        ];
1115

1116
                                        let mut owned_name = name.clone();
495✔
1117

1118
                                        // If we have the alias listed, we should use it
1119
                                        if !explicit_requires.is_empty() {
495✔
1120
                                            if let Some(alias) = explicit_requires
×
1121
                                                .get(name.atom_identifier().unwrap())
×
1122
                                                .copied()
1123
                                                .flatten()
1124
                                            {
1125
                                                *owned_name.atom_identifier_mut().unwrap() =
1126
                                                    alias.clone();
1127
                                            }
1128
                                        }
1129

1130
                                        if let Some(prefix) = &require_object.prefix {
×
1131
                                            if let Some(existing) = owned_name.atom_identifier_mut()
×
1132
                                            {
1133
                                                let mut prefixed_identifier = prefix.clone();
×
1134
                                                prefixed_identifier.push_str(existing.resolve());
×
1135

1136
                                                // Update the existing identifier to point to a new one with the prefix applied
1137
                                                *existing = prefixed_identifier.into();
×
1138
                                            }
1139
                                        }
1140

1141
                                        globals.insert(*name.atom_identifier().unwrap());
495✔
1142

1143
                                        let define = ExprKind::Define(Box::new(Define::new(
495✔
1144
                                            owned_name,
495✔
1145
                                            hash_get,
495✔
1146
                                            SyntaxObject::default(TokenType::Define),
495✔
1147
                                        )));
1148

1149
                                        provide_definitions.push(define);
495✔
1150
                                    }
1151

1152
                                    // x if x == *CONTRACT_OUT => {
1153
                                    //     // Directly expand into define/contract, but with the value just being the hash get below
1154

1155
                                    //     // (bind/c contract name 'name)
1156

1157
                                    //     let mut name = l.args.get(1).unwrap().clone();
1158
                                    //     let _contract = l.args.get(2).unwrap();
1159

1160
                                    //     if !explicit_requires.is_empty()
1161
                                    //         && !name
1162
                                    //             .atom_identifier()
1163
                                    //             .map(|x| explicit_requires.contains_key(x))
1164
                                    //             .unwrap_or_default()
1165
                                    //     {
1166
                                    //         continue;
1167
                                    //     }
1168

1169
                                    //     // If we have the alias listed, we should use it
1170
                                    //     if !explicit_requires.is_empty() {
1171
                                    //         if let Some(alias) = explicit_requires
1172
                                    //             .get(name.atom_identifier().unwrap())
1173
                                    //             .copied()
1174
                                    //             .flatten()
1175
                                    //         {
1176
                                    //             *name.atom_identifier_mut().unwrap() =
1177
                                    //                 alias.clone();
1178
                                    //         }
1179
                                    //     }
1180

1181
                                    //     if let Some(prefix) = &require_object.prefix {
1182
                                    //         if let Some(existing) = name.atom_identifier_mut() {
1183
                                    //             let mut prefixed_identifier = prefix.clone();
1184
                                    //             prefixed_identifier.push_str(existing.resolve());
1185

1186
                                    //             // Update the existing identifier to point to a new one with the prefix applied
1187
                                    //             *existing = prefixed_identifier.into();
1188
                                    //         }
1189
                                    //     }
1190

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

1196
                                    //     let hash_get = expr_list![
1197
                                    //         ExprKind::atom(*PROTO_HASH_GET),
1198
                                    //         ExprKind::atom(
1199
                                    //             "__module-".to_string() + &other_module_prefix
1200
                                    //         ),
1201
                                    //         ExprKind::Quote(Box::new(Quote::new(
1202
                                    //             name.clone(),
1203
                                    //             SyntaxObject::default(TokenType::Quote)
1204
                                    //         ))),
1205
                                    //     ];
1206

1207
                                    //     let define = ExprKind::Define(Box::new(Define::new(
1208
                                    //         ExprKind::atom(
1209
                                    //             prefix.clone()
1210
                                    //                 + name.atom_identifier().unwrap().resolve(),
1211
                                    //         ),
1212
                                    //         hash_get,
1213
                                    //         SyntaxObject::default(TokenType::Define),
1214
                                    //     )));
1215

1216
                                    //     provide_definitions.push(define);
1217
                                    // }
1218
                                    _ => {
1219
                                        stop!(TypeMismatch => format!("provide expects either an identifier, (for-syntax <ident>), or (contract/out ...) - found: {}", provide))
×
1220
                                    }
1221
                                }
1222
                            } else {
1223
                                stop!(TypeMismatch => "provide expects either an identifier or a (for-syntax <ident>)")
×
1224
                            }
1225
                        }
1226
                        ExprKind::Atom(_) => {
1227
                            if !explicit_requires.is_empty()
21,553✔
1228
                                && !provide
5✔
1229
                                    .atom_identifier()
5✔
1230
                                    .map(|x| explicit_requires.contains_key(x))
10✔
1231
                                    .unwrap_or_default()
1232
                            {
1233
                                continue;
2✔
1234
                            }
1235

1236
                            if module
21,551✔
1237
                                .macro_map
21,551✔
1238
                                .contains_key(provide.atom_identifier().unwrap())
21,551✔
1239
                            {
1240
                                continue;
162✔
1241
                            }
1242

1243
                            // Mangle with a prefix if necessary
1244
                            let mut provide = provide.clone();
21,389✔
1245
                            let raw_provide = provide.clone();
21,389✔
1246

1247
                            // If we have the alias listed, we should use it
1248
                            if !explicit_requires.is_empty() {
21,389✔
1249
                                if let Some(alias) = explicit_requires
3✔
1250
                                    .get(provide.atom_identifier().unwrap())
3✔
1251
                                    .copied()
1252
                                    .flatten()
1253
                                {
1254
                                    *provide.atom_identifier_mut().unwrap() = alias.clone();
1255
                                }
1256
                            }
1257

1258
                            if let Some(prefix) = &require_object.prefix {
×
1259
                                if let Some(existing) = provide.atom_identifier_mut() {
×
1260
                                    let mut prefixed_identifier = prefix.clone();
×
1261
                                    prefixed_identifier.push_str(existing.resolve());
×
1262

1263
                                    // Update the existing identifier to point to a new one with the prefix applied
1264
                                    *existing = prefixed_identifier.into();
×
1265
                                }
1266
                            }
1267

1268
                            let provide_ident = provide.atom_identifier().unwrap();
21,389✔
1269

1270
                            // Since this is now bound to be in the scope of the current working module, we also want
1271
                            // this to be mangled. In the event we do something like, qualify the import, then we might
1272
                            // have to mangle this differently
1273
                            globals.insert(*provide_ident);
21,389✔
1274

1275
                            let define = ExprKind::Define(Box::new(Define::new(
21,389✔
1276
                                ExprKind::atom(prefix.clone() + provide_ident.resolve()),
21,389✔
1277
                                expr_list![
21,389✔
1278
                                    ExprKind::atom(*PROTO_HASH_GET),
21,389✔
1279
                                    ExprKind::atom(
21,389✔
1280
                                        CompactString::new(MODULE_PREFIX) + &other_module_prefix
21,389✔
1281
                                    ),
1282
                                    ExprKind::Quote(Box::new(Quote::new(
21,389✔
1283
                                        raw_provide.clone(),
21,389✔
1284
                                        SyntaxObject::default(TokenType::Quote)
21,389✔
1285
                                    )))
1286
                                ],
1287
                                SyntaxObject::default(TokenType::Define),
21,389✔
1288
                            )));
1289

1290
                            // if require_object.prefix.is_some() {
1291
                            //     println!("{}", define);
1292
                            // }
1293

1294
                            provide_definitions.push(define);
21,389✔
1295
                        }
1296
                        _ => {
1297
                            stop!(TypeMismatch => "provide expression needs to either be a `contract/out` form or an identifier")
×
1298
                        }
1299
                    }
1300
                }
1301
            }
1302
        }
1303

1304
        // Mangle all of the variables that are either:
1305
        // 1. Defined locally in this file
1306
        // 2. Required by another file
1307
        let mut name_mangler = NameMangler::new(globals, prefix.clone());
1,027✔
1308

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

1313
        name_mangler.mangle_vars(&mut exprs);
1,027✔
1314

1315
        // The provide definitions should also be mangled
1316
        name_mangler.mangle_vars(&mut provide_definitions);
1,027✔
1317

1318
        // let mut hash_builder = Vec::new();
1319

1320
        // These are gonna be the pairs
1321
        // hash_builder.push(());
1322

1323
        // Construct the series of provides as well, we'll want these to refer to the correct values
1324
        //
1325
        let mut provides: smallvec::SmallVec<[(ExprKind, ExprKind); 24]> = self
1,027✔
1326
            .provides
1,027✔
1327
            .iter()
1328
            .flat_map(|x| &x.list().unwrap().args[1..])
3,731✔
1329
            .cloned()
1330
            .map(|x| (x.clone(), x))
11,975✔
1331
            .collect();
1332

1333
        for provide in &mut provides {
20,869✔
1334
            match &provide.1 {
1335
                ExprKind::List(l) => {
497✔
1336
                    if let Some(qualifier) = l.first_ident() {
497✔
1337
                        match qualifier {
1338
                            x if *x == *REQUIRE_IDENT_SPEC => {
497✔
1339
                                // *provide = expand(l.get(2).unwrap().clone(), global_macro_map)?;
1340

1341
                                // *provide = expand(l.)
1342

1343
                                provide.0 = l.get(1).unwrap().clone();
497✔
1344

1345
                                let mut provide_expr = l.get(2).unwrap().clone();
497✔
1346
                                expand(&mut provide_expr, global_macro_map)?;
497✔
1347

1348
                                provide.1 = provide_expr;
497✔
1349

1350
                                continue;
497✔
1351

1352
                                // name_unmangler.unmangle_expr(provide);
1353
                            }
1354
                            // x if *x == *CONTRACT_OUT => {
1355
                            //     // Update the item to point to just the name
1356
                            //     //
1357
                            //     // *provide = l.get(1).unwrap().clone();
1358
                            //     // {
1359
                            //     //     println!("---------");
1360
                            //     //     println!("Provide expr: {}", l.to_string());
1361
                            //     // }
1362

1363
                            //     provide.0 = l.get(1).unwrap().clone();
1364

1365
                            //     let mut provide_expr = expr_list![
1366
                            //         ExprKind::ident("bind/c"),
1367
                            //         l.get(2).unwrap().clone(),
1368
                            //         l.get(1).unwrap().clone(),
1369
                            //         ExprKind::Quote(Box::new(Quote::new(
1370
                            //             l.get(1).unwrap().clone(),
1371
                            //             SyntaxObject::default(TokenType::Quote)
1372
                            //         ))),
1373
                            //     ];
1374

1375
                            //     expand(&mut provide_expr, global_macro_map)?;
1376

1377
                            //     provide.1 = provide_expr;
1378

1379
                            //     name_unmangler.unmangle_expr(&mut provide.1);
1380
                            //     // continue;
1381
                            // }
1382
                            unknown => {
×
1383
                                stop!(TypeMismatch => "bar provide expects either an identifier, (for-syntax <ident>), or (contract/out ...), found: {}", unknown)
×
1384
                            }
1385
                        }
1386
                    } else {
1387
                        stop!(TypeMismatch => "provide expects either an identifier or a (for-syntax <ident>)")
×
1388
                    }
1389
                }
1390
                ExprKind::Atom(_) => {
1391
                    continue;
9,424✔
1392
                }
1393
                _ => {
1394
                    stop!(TypeMismatch => "provide expression needs to either be a `contract/out` form or an identifier")
×
1395
                }
1396
            }
1397
        }
1398

1399
        // Drop all of the macro references here
1400
        provides.retain(|x| !self.macro_map.contains_key(x.0.atom_identifier().unwrap()));
11,975✔
1401

1402
        // We want one without the mangled version, for the actual provides
1403
        let un_mangled = provides.clone();
1,027✔
1404

1405
        let left_unmangled: Vec<_> = un_mangled.into_iter().map(|x| x.0).collect();
11,487✔
1406

1407
        let mut right: Vec<_> = provides.into_iter().map(|x| x.1).collect();
11,487✔
1408

1409
        name_mangler.mangle_vars(&mut right);
1,027✔
1410
        // name_unmangler.unmangle_vars(&mut provides);
1411

1412
        let mut hash_body = vec![ExprKind::ident("hash")];
1,027✔
1413

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

1419
        // left_unmangled.pretty_print();
1420

1421
        hash_body.extend(interleave(
1,027✔
1422
            left_unmangled.into_iter().map(|x| {
10,460✔
1423
                if let ExprKind::Atom(_) = x {
9,433✔
1424
                    ExprKind::Quote(Box::new(Quote::new(
9,433✔
1425
                        x,
9,433✔
1426
                        SyntaxObject::default(TokenType::Quote),
9,433✔
1427
                    )))
1428
                } else if let ExprKind::List(l) = x {
×
1429
                    if let Some(qualifier) = l.first_ident() {
×
1430
                        match qualifier {
1431
                            x if *x == *REQUIRE_IDENT_SPEC => {
×
1432
                                todo!()
×
1433
                            }
1434
                            _ => {
1435
                                return ExprKind::Quote(Box::new(Quote::new(
×
1436
                                    l.get(2).unwrap().clone(),
×
1437
                                    SyntaxObject::default(TokenType::Quote),
×
1438
                                )))
1439
                            }
1440
                        }
1441
                    }
1442

1443
                    // Then this is a contract out, and we should handle it here
1444

1445
                    ExprKind::Quote(Box::new(Quote::new(
×
1446
                        l.get(2).unwrap().clone(),
×
1447
                        SyntaxObject::default(TokenType::Quote),
×
1448
                    )))
1449
                    // ExprKind::Quote(Box::new(Quote::new(
1450
                    //     x,
1451
                    //     SyntaxObject::default(TokenType::Quote),
1452
                    // )))
1453
                } else {
1454
                    panic!("TODO this shouldn't be possible")
×
1455
                }
1456
            }),
1457
            right,
1,027✔
1458
        ));
1459

1460
        let module_define = ExprKind::Define(Box::new(Define::new(
1,027✔
1461
            ExprKind::atom(CompactString::new(MODULE_PREFIX) + &prefix),
1,027✔
1462
            ExprKind::List(List::new(hash_body)),
1,027✔
1463
            SyntaxObject::default(TokenType::Quote),
1,027✔
1464
        )));
1465

1466
        // let mut offset = None;
1467

1468
        // Find offset of first non builtin require definition:
1469
        // for (idx, expr) in exprs.iter().enumerate() {
1470
        //     if let ExprKind::Define(d) = expr {
1471
        //         // if !is_a_builtin_definition(d) || !is_a_require_definition(d) {
1472
        //         if !is_a_builtin_definition(d) {
1473
        //             // println!("Found offset at: {:?}", offset);
1474

1475
        //             offset = Some(idx);
1476
        //             println!("Found offset at: {:?}", offset);
1477
        //             break;
1478
        //         }
1479
        //     }
1480
        // }
1481

1482
        exprs.push(module_define);
1,027✔
1483

1484
        // exprs.append(&mut provide_definitions);
1485

1486
        let mut builtin_definitions = Vec::new();
1,027✔
1487

1488
        exprs.retain_mut(|expr| {
203,019✔
1489
            if let ExprKind::Define(d) = expr {
398,520✔
1490
                if is_a_builtin_definition(d) {
1491
                    builtin_definitions.push(std::mem::take(expr));
171,875✔
1492
                    false
171,875✔
1493
                } else {
1494
                    true
24,653✔
1495
                }
1496
            } else {
1497
                true
5,464✔
1498
            }
1499
        });
1500

1501
        builtin_definitions.append(&mut provide_definitions);
1,027✔
1502
        builtin_definitions.append(&mut exprs);
1,027✔
1503

1504
        // provide_definitions.append(&mut builtin_definitions);
1505
        // provide_definitions.append(&mut exprs);
1506

1507
        exprs = builtin_definitions;
1,027✔
1508

1509
        // if let Some(offset) = offset {
1510
        // for (idx, expr) in provide_definitions.into_iter().enumerate() {
1511
        //     exprs.insert(offset + idx, expr);
1512
        // }
1513
        // } else {
1514
        // provide_definitions.append(&mut exprs);
1515
        // }
1516

1517
        // println!("MODULE DEFINITIONS----");
1518

1519
        // exprs.pretty_print();
1520

1521
        // println!("END MODULE DEFINITIONS");
1522

1523
        // exprs.pretty_print();
1524

1525
        // exprs.push(module_define);
1526

1527
        // Construct the overall definition
1528
        // TODO: Perhaps mangle these as well, especially if they have contracts associated with them
1529

1530
        // if offset.is_none() {
1531
        //     provide_definitions.append(&mut exprs);
1532
        // }
1533

1534
        // Try this out?
1535
        // let mut analysis = Analysis::from_exprs(&provide_definitions);
1536
        // let mut semantic = SemanticAnalysis::from_analysis(&mut provide_definitions, analysis);
1537

1538
        // // This is definitely broken still
1539
        // semantic.remove_unused_globals_with_prefix("mangler");
1540
        // .replace_non_shadowed_globals_with_builtins()
1541
        // .remove_unused_globals_with_prefix("mangler");
1542

1543
        Ok(ExprKind::Begin(Box::new(Begin::new(
1,027✔
1544
            exprs,
1,027✔
1545
            SyntaxObject::default(TokenType::Begin),
1,027✔
1546
        ))))
1547
    }
1548

1549
    // Turn the module into the AST node that represents the macro module in the stdlib
1550
    fn _to_module_ast_node(&self) -> ExprKind {
×
1551
        let mut body = vec![
×
1552
            ExprKind::Atom(Atom::new(SyntaxObject::default(TokenType::Identifier(
×
1553
                "module".into(),
×
1554
            )))),
1555
            ExprKind::Atom(Atom::new(SyntaxObject::default(TokenType::Identifier(
×
1556
                ("___".to_string() + self.name.to_str().unwrap()).into(),
×
1557
            )))),
1558
        ];
1559

1560
        // Put any provides at the top
1561
        body.append(&mut self.provides.clone());
×
1562

1563
        // Include any dependencies here
1564
        // body.append(&mut self.requires.clone());
1565

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

1572
        // self.ast.pretty_print();
1573

1574
        // body.push(steel_base);
1575

1576
        // Put the ast nodes inside the macro
1577
        body.append(&mut self.ast.clone());
×
1578

1579
        // TODO clean this up
1580
        let res = ExprKind::List(List::new(body));
×
1581

1582
        // if log_enabled!(target: "requires", log::Level::Debug) {
1583
        //     debug!(target: "requires", "Module ast node: {}", res.to_string());
1584
        // }
1585

1586
        res
×
1587
    }
1588
}
1589

1590
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
1591
enum MaybeRenamed {
1592
    Normal(ExprKind),
1593
    Renamed(ExprKind, ExprKind),
1594
}
1595

1596
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1597
pub struct RequireObject {
1598
    path: PathOrBuiltIn,
1599
    for_syntax: bool,
1600
    idents_to_import: Vec<MaybeRenamed>,
1601
    prefix: Option<String>,
1602
    span: Span,
1603
}
1604

1605
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1606
enum PathOrBuiltIn {
1607
    BuiltIn(Cow<'static, str>),
1608
    Path(PathBuf),
1609
}
1610

1611
impl PathOrBuiltIn {
1612
    pub fn get_path(&self) -> Cow<'_, PathBuf> {
7,725✔
1613
        match self {
7,725✔
1614
            Self::Path(p) => Cow::Borrowed(p),
246✔
1615
            Self::BuiltIn(p) => Cow::Owned(PathBuf::from(p.to_string())),
7,479✔
1616
        }
1617
    }
1618
}
1619

1620
#[derive(Default, Debug, Clone)]
1621
struct RequireObjectBuilder {
1622
    path: Option<PathOrBuiltIn>,
1623
    for_syntax: bool,
1624
    idents_to_import: Vec<MaybeRenamed>,
1625
    // Built up prefix
1626
    prefix: Option<String>,
1627
    span: Span,
1628
}
1629

1630
impl RequireObjectBuilder {
1631
    fn build(self) -> Result<RequireObject> {
2,575✔
1632
        let path = self
5,150✔
1633
            .path
2,575✔
1634
            .ok_or_else(crate::throw!(Generic => "require must have a path!"))?;
2,575✔
1635

1636
        Ok(RequireObject {
1637
            path,
1638
            for_syntax: self.for_syntax,
1639
            idents_to_import: self.idents_to_import,
1640
            prefix: self.prefix,
1641
            span: self.span,
1642
        })
1643
    }
1644
}
1645

1646
fn try_canonicalize(path: PathBuf) -> PathBuf {
1,109✔
1647
    std::fs::canonicalize(&path).unwrap_or_else(|_| path)
3,191✔
1648
}
1649

1650
struct ModuleBuilder<'a> {
1651
    name: PathBuf,
1652
    main: bool,
1653
    source_ast: Vec<ExprKind>,
1654
    macro_map: FxHashMap<InternedString, SteelMacro>,
1655
    // TODO: Change the requires / requires_for_syntax to just be a require enum?
1656
    require_objects: Vec<RequireObject>,
1657

1658
    provides: Vec<ExprKind>,
1659
    provides_for_syntax: Vec<ExprKind>,
1660
    compiled_modules: &'a mut FxHashMap<PathBuf, CompiledModule>,
1661
    visited: &'a mut FxHashSet<PathBuf>,
1662
    file_metadata: &'a mut FxHashMap<PathBuf, SystemTime>,
1663
    sources: &'a mut Sources,
1664
    kernel: &'a mut Option<Kernel>,
1665
    builtin_modules: ModuleContainer,
1666
    global_macro_map: &'a FxHashMap<InternedString, SteelMacro>,
1667
    custom_builtins: &'a HashMap<String, String>,
1668
    search_dirs: &'a [PathBuf],
1669
}
1670

1671
impl<'a> ModuleBuilder<'a> {
1672
    #[allow(clippy::too_many_arguments)]
1673
    #[allow(unused)]
1674
    fn main(
795✔
1675
        name: Option<PathBuf>,
1676
        source_ast: Vec<ExprKind>,
1677
        compiled_modules: &'a mut FxHashMap<PathBuf, CompiledModule>,
1678
        visited: &'a mut FxHashSet<PathBuf>,
1679
        file_metadata: &'a mut FxHashMap<PathBuf, SystemTime>,
1680
        sources: &'a mut Sources,
1681
        kernel: &'a mut Option<Kernel>,
1682
        builtin_modules: ModuleContainer,
1683
        global_macro_map: &'a FxHashMap<InternedString, SteelMacro>,
1684
        custom_builtins: &'a HashMap<String, String>,
1685
        search_dirs: &'a [PathBuf],
1686
    ) -> Result<Self> {
1687
        // TODO don't immediately canonicalize the path unless we _know_ its coming from a path
1688
        // change the path to not always be required
1689
        // if its not required we know its not coming in
1690

1691
        #[cfg(not(target_arch = "wasm32"))]
1692
        let name = if let Some(p) = name {
1,601✔
1693
            std::fs::canonicalize(p)?
×
1694
        } else {
1695
            std::env::current_dir()?
784✔
1696
        };
1697

1698
        #[cfg(target_arch = "wasm32")]
1699
        let name = PathBuf::new();
×
1700

1701
        Ok(ModuleBuilder {
×
1702
            name,
×
1703
            main: true,
×
1704
            source_ast,
×
1705
            macro_map: default_prelude_macros(),
×
1706
            require_objects: Vec::new(),
×
1707
            provides: Vec::new(),
×
1708
            provides_for_syntax: Vec::new(),
×
1709
            compiled_modules,
×
1710
            visited,
×
1711
            file_metadata,
×
1712
            sources,
×
1713
            kernel,
×
1714
            builtin_modules,
×
1715
            global_macro_map,
×
1716
            custom_builtins,
×
1717
            search_dirs,
×
1718
        })
1719
    }
1720

1721
    fn compile(&mut self) -> Result<Vec<ExprKind>> {
1,822✔
1722
        // debug!(target: "requires", "Visiting: {:?}", self.name);
1723

1724
        // @Matt - 10/3/23
1725
        // This has a relatively fatal flaw at the moment:
1726
        /*
1,822✔
1727

1728
            (define-syntax register-plugin
1,822✔
1729
              (syntax-rules ()
1,822✔
1730
                [(register-plugin plugin-path identifiers ...)
1,822✔
1731
                 (begin
1,822✔
1732
                   (require plugin-path
1,822✔
1733
                            (only-in identifiers ...))
1,822✔
1734
                   (provide identifiers ...))]))
1,822✔
1735
        */
1,822✔
1736
        // This will fail to compile - anything that expands into a require will fail since
1737
        // require is more or less a top level primitive. What we need to do is figure
1738
        // out a way to have this be recursive - the whole compilation step should
1739
        // go again once we discover that there is another require. It shouldn't be too bad,
1740
        // but the base case has to be figured out such that we don't get infinite recursion.
1741
        // I think the condition is probably something along the following:
1742
        //
1743
        // - Did we expand anything
1744
        // - Are there require statements
1745
        //
1746
        // If we expanded anything, we then should check for require statements... maybe
1747

1748
        let mut new_exprs = Vec::new();
1,822✔
1749

1750
        // self.source_ast.pretty_print();
1751

1752
        self.collect_requires()?;
1,822✔
1753
        self.collect_provides()?;
1,822✔
1754

1755
        // if log_enabled!(log::Level::Info) {
1756
        // debug!(target: "requires", "Requires: {:#?}", self.require_objects);
1757
        // debug!(target: "requires", "Provides: {:#?}", self.provides);
1758
        // debug!(target: "requires", "Provides for-syntax: {:?}", self.provides_for_syntax);
1759
        // }
1760

1761
        if self.visited.contains(&self.name) {
1,822✔
1762
            stop!(Generic => format!("circular dependency found during module resolution with: {:?}", self.name))
×
1763
        }
1764

1765
        self.visited.insert(self.name.clone());
1,822✔
1766

1767
        if self.main {
2,617✔
1768
            let exprs = std::mem::take(&mut self.source_ast);
795✔
1769
            self.source_ast = exprs
795✔
1770
                .into_iter()
795✔
1771
                .filter(|x| {
7,747✔
1772
                    if let ExprKind::List(l) = x {
13,883✔
1773
                        if let Some(provide) = l.first_ident() {
2,380✔
1774
                            return *provide != *PROVIDE;
2,380✔
1775
                        }
1776
                    }
1777
                    true
4,572✔
1778
                })
1779
                .collect();
795✔
1780
        }
1781

1782
        self.extract_macro_defs()?;
×
1783

1784
        // TODO include built ins here
1785
        if self.require_objects.is_empty() && !self.main {
2,901✔
1786
            // We're at a leaf, put into the cache
1787
            new_exprs.push(self.compile_module()?);
487✔
1788
        } else {
1789
            // TODO come back for parsing built ins
1790
            for module in self
3,828✔
1791
                .require_objects
1,335✔
1792
                .iter()
1,335✔
1793
                .filter(|x| matches!(x.path, PathOrBuiltIn::BuiltIn(_)))
6,485✔
1794
                .map(|x| x.path.get_path())
2,493✔
1795
            {
1796
                // We've established nothing has changed with this file
1797
                // Check to see if its in the cache first
1798
                // Otherwise go ahead and compile
1799
                // If we already have compiled this module, get it from the cache
1800
                if let Some(_m) = self.compiled_modules.get(module.as_ref()) {
4,013✔
1801
                    // debug!("Getting {:?} from the module cache", module);
1802
                    // println!("Already found in the cache: {:?}", module);
1803
                    // new_exprs.push(m.to_module_ast_node());
1804
                    // No need to do anything
1805
                    continue;
×
1806
                }
1807

1808
                // TODO this is some bad crap here don't do this
1809
                let input = BUILT_INS
1,946✔
1810
                    .iter()
1811
                    .find(|x| x.0 == module.to_str().unwrap())
9,725✔
1812
                    .map(|x| Cow::Borrowed(x.1))
2,919✔
1813
                    .or_else(|| {
973✔
1814
                        self.custom_builtins
×
1815
                            .get(module.to_str().unwrap())
×
1816
                            .map(|x| Cow::Owned(x.to_string()))
×
1817
                    })
1818
                    .ok_or_else(
1819
                        crate::throw!(Generic => "Unable to find builtin module: {:?}", module),
973✔
1820
                    )?;
1821

1822
                let mut new_module = ModuleBuilder::new_built_in(
1823
                    module.into_owned(),
×
1824
                    input,
×
1825
                    self.compiled_modules,
×
1826
                    self.visited,
×
1827
                    self.file_metadata,
×
1828
                    self.sources,
×
1829
                    self.kernel,
×
1830
                    self.builtin_modules.clone(),
×
1831
                    self.global_macro_map,
×
1832
                    self.custom_builtins,
×
1833
                )?;
1834

1835
                // Walk the tree and compile any dependencies
1836
                // This will eventually put the module in the cache
1837
                let mut module_exprs = new_module.compile()?;
973✔
1838

1839
                new_exprs.append(&mut module_exprs);
×
1840

1841
                // Probably want to evaluate a module even if it has no provides?
1842
                if !new_module.provides.is_empty() {
×
1843
                    new_exprs.push(new_module.compile_module()?);
486✔
1844
                } else {
1845
                    // log::debug!(target: "requires", "Found no provides, skipping compilation of module: {:?}", new_module.name);
1846
                }
1847
            }
1848

1849
            // At this point, requires should be fully qualified (absolute) paths
1850

1851
            for (module, require_statement_span) in self
1,417✔
1852
                .require_objects
1,335✔
1853
                .iter()
1,335✔
1854
                .filter(|x| matches!(x.path, PathOrBuiltIn::Path(_)))
7,820✔
1855
                .map(|x| (x.path.get_path(), x.span))
2,752✔
1856
            {
1857
                if cfg!(target_arch = "wasm32") {
1858
                    stop!(Generic => "requiring modules is not supported for wasm");
×
1859
                }
1860

1861
                let last_modified = std::fs::metadata(module.as_ref())
82✔
1862
                    .map_err(|err| {
×
1863
                        let mut err = crate::SteelErr::from(err);
×
1864
                        err.prepend_message(&format!(
×
1865
                            "Attempting to load module from: {:?} ",
×
1866
                            module
×
1867
                        ));
1868
                        err.set_span(require_statement_span)
×
1869
                    })?
1870
                    .modified()?;
1871

1872
                // Check if we should compile based on the last time modified
1873
                // If we're unable to get information, we want to compile
1874
                let should_recompile =
×
1875
                    if let Some(cached_modified) = self.file_metadata.get(module.as_ref()) {
28✔
1876
                        &last_modified != cached_modified
×
1877
                    } else {
1878
                        true
54✔
1879
                    };
1880

1881
                // We've established nothing has changed with this file
1882
                // Check to see if its in the cache first
1883
                // Otherwise go ahead and compile
1884
                if !should_recompile {
×
1885
                    // If we already have compiled this module, get it from the cache
1886
                    if let Some(_m) = self.compiled_modules.get(module.as_ref()) {
56✔
1887
                        // debug!("Getting {:?} from the module cache", module);
1888
                        // println!("Already found in the cache: {:?}", module);
1889
                        // new_exprs.push(m.to_module_ast_node());
1890
                        // No need to do anything
1891
                        continue;
×
1892
                    }
1893
                }
1894

1895
                let mut new_module = ModuleBuilder::new_from_path(
1896
                    module.into_owned(),
54✔
1897
                    self.compiled_modules,
54✔
1898
                    self.visited,
54✔
1899
                    self.file_metadata,
54✔
1900
                    self.sources,
54✔
1901
                    self.kernel,
54✔
1902
                    self.builtin_modules.clone(),
54✔
1903
                    self.global_macro_map,
54✔
1904
                    self.custom_builtins,
54✔
1905
                    self.search_dirs,
54✔
1906
                )?;
1907

1908
                // Walk the tree and compile any dependencies
1909
                // This will eventually put the module in the cache
1910
                let mut module_exprs = new_module.compile()?;
54✔
1911

1912
                // debug!("Inside {:?} - append {:?}", self.name, module);
1913
                // if log_enabled!(log::Level::Debug) {
1914
                //     debug!(
1915
                //         target: "modules",
1916
                //         "appending with {:?}",
1917
                //         module_exprs.iter().map(|x| x.to_string()).join(" SEP ")
1918
                //     );
1919
                // }
1920

1921
                new_exprs.append(&mut module_exprs);
×
1922

1923
                // TODO evaluate this
1924

1925
                // let mut ast = std::mem::replace(&mut new_module.source_ast, Vec::new());
1926
                // ast.append(&mut module_exprs);
1927
                // new_module.source_ast = ast;
1928

1929
                // dbg!(&new_module.name);
1930
                // dbg!(&new_module.compiled_modules.contains_key(&new_module.name));
1931

1932
                // If we need to, revisit because there are new provides
1933
                if !new_module.provides.is_empty() {
×
1934
                    new_exprs.push(new_module.compile_module()?);
52✔
1935
                // If the module hasn't yet been compiled, compile it anyway
1936
                } else if !new_module.compiled_modules.contains_key(&new_module.name) {
2✔
1937
                    // else if !new_module.compiled_modules.contains_key(&new_module.name) {
1938
                    new_exprs.push(new_module.compile_module()?);
2✔
1939
                } else {
1940
                    // log::debug!(target: "requires", "Found no provides, skipping compilation of module: {:?}", new_module.name);
1941
                    // log::debug!(target: "requires", "Module already in the cache: {}", new_module.compiled_modules.contains_key(&new_module.name));
1942
                    // log::debug!(target: "requires", "Compiled modules: {:?}", new_module.compiled_modules.keys().collect::<Vec<_>>());
1943
                }
1944

1945
                // else {
1946
                //     log::debug!(target: "requires", "Found no provides, skipping compilation of module: {:?}", new_module.name);
1947
                // }
1948
            }
1949
        }
1950

1951
        // new_exprs.pretty_print();
1952

1953
        Ok(new_exprs)
1,822✔
1954
    }
1955

1956
    // TODO: This should run again on itself, probably
1957
    fn compile_module(&mut self) -> Result<ExprKind> {
1,027✔
1958
        let mut ast = std::mem::take(&mut self.source_ast);
1,027✔
1959
        let mut provides = std::mem::take(&mut self.provides);
1,027✔
1960
        // Clone the requires... I suppose
1961
        let requires = self.require_objects.clone();
1,027✔
1962

1963
        // info!(
1964
        //     target: "requires",
1965
        //     "Into compiled module: provides for syntax: {:?}",
1966
        //     self.provides_for_syntax
1967
        // );
1968

1969
        // Attempt extracting the syntax transformers from this module
1970
        if let Some(kernel) = self.kernel.as_mut() {
2,054✔
1971
            kernel.load_syntax_transformers(&mut ast, self.name.to_str().unwrap().to_string())?
×
1972
        };
1973

1974
        for expr in ast.iter_mut() {
15,699✔
1975
            expand(expr, &self.macro_map)?;
14,672✔
1976

1977
            expand_kernel_in_env(
1978
                expr,
14,672✔
1979
                self.kernel.as_mut(),
14,672✔
1980
                self.builtin_modules.clone(),
14,672✔
1981
                // Expanding macros in the environment?
1982
                self.name.to_str().unwrap(),
14,672✔
1983
            )?;
1984

1985
            expand(expr, &self.macro_map)?;
14,672✔
1986
        }
1987

1988
        // Expand first with the macros from *this* module
1989
        // ast = ast
1990
        //     .into_iter()
1991
        //     .map(|x| {
1992
        //         expand(x, &self.macro_map)
1993
        //             .and_then(|x| {
1994
        //                 expand_kernel_in_env(
1995
        //                     x,
1996
        //                     self.kernel.as_mut(),
1997
        //                     self.builtin_modules.clone(),
1998
        //                     // Expanding macros in the environment?
1999
        //                     self.name.to_str().unwrap().to_string(),
2000
        //                 )
2001
        //             })
2002
        //             // Check here - I think it makes sense to expand the
2003
        //             // internal macros again, given now we might have defmacro
2004
        //             // style macros that get expanded into syntax-rules ones?
2005
        //             .and_then(|x| expand(x, &self.macro_map))
2006
        //     })
2007
        //     .collect::<Result<Vec<_>>>()?;
2008

2009
        for expr in provides.iter_mut() {
2,700✔
2010
            expand(expr, &self.macro_map)?;
1,673✔
2011
            // .and_then(|x| {
2012
            // expand_kernel(x, self.kernel.as_mut(), self.builtin_modules.clone())
2013
            expand_kernel_in_env(
2014
                expr,
1,673✔
2015
                self.kernel.as_mut(),
1,673✔
2016
                self.builtin_modules.clone(),
1,673✔
2017
                // Expanding macros in the environment?
2018
                &self.name.to_str().unwrap(),
1,673✔
2019
            )?;
2020
            // })
2021
        }
2022

2023
        // Expand provides for any macros that exist within there
2024
        // provides = provides
2025
        //     .into_iter()
2026
        //     .map(|x| {
2027
        //         expand(x, &self.macro_map).and_then(|x| {
2028
        //             // expand_kernel(x, self.kernel.as_mut(), self.builtin_modules.clone())
2029
        //             expand_kernel_in_env(
2030
        //                 x,
2031
        //                 self.kernel.as_mut(),
2032
        //                 self.builtin_modules.clone(),
2033
        //                 // Expanding macros in the environment?
2034
        //                 self.name.to_str().unwrap().to_string(),
2035
        //             )
2036
        //         })
2037
        //     })
2038
        //     .collect::<Result<Vec<_>>>()?;
2039

2040
        let mut mangled_asts = Vec::with_capacity(ast.len() + 16);
1,027✔
2041

2042
        // Look for the modules in the requires for syntax
2043
        for require_object in self.require_objects.iter()
2,420✔
2044
        // .filter(|x| x.for_syntax)
2045
        {
2046
            let require_for_syntax = require_object.path.get_path();
1,393✔
2047

2048
            let (module, in_scope_macros, mut name_mangler) = ModuleManager::find_in_scope_macros(
2049
                self.compiled_modules,
1,393✔
2050
                require_for_syntax.as_ref(),
1,393✔
2051
                &require_object,
1,393✔
2052
                &mut mangled_asts,
1,393✔
2053
            );
2054

2055
            // let kernel_macros_in_scope: HashSet<_> =
2056
            //     module.provides_for_syntax.iter().cloned().collect();
2057

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

2060
            for expr in ast.iter_mut() {
15,010✔
2061
                // First expand the in scope macros
2062
                // These are macros
2063
                let mut expander = Expander::new(&in_scope_macros);
13,617✔
2064
                expander.expand(expr)?;
13,617✔
2065
                let changed = false;
13,617✔
2066

2067
                // dbg!(expander.changed);
2068

2069
                // (first_round_expanded, changed) = expand_kernel_in_env_with_allowed(
2070
                //     first_round_expanded,
2071
                //     self.kernel.as_mut(),
2072
                //     // We don't need to expand those here
2073
                //     ModuleContainer::default(),
2074
                //     module.name.to_str().unwrap().to_string(),
2075
                //     &kernel_macros_in_scope,
2076
                // )?;
2077

2078
                // If the kernel expander expanded into something - go ahead
2079
                // and expand all of the macros in this
2080
                // if changed || expander.changed {
2081
                // Expand here?
2082
                // first_round_expanded = expand(first_round_expanded, &module.macro_map)?;
2083

2084
                // Probably don't need this
2085
                // (first_round_expanded, changed) = expand_kernel_in_env_with_change(
2086
                //     first_round_expanded,
2087
                //     self.kernel.as_mut(),
2088
                //     ModuleContainer::default(),
2089
                //     module.name.to_str().unwrap().to_string(),
2090
                // )?;
2091

2092
                // name_mangler.visit(&mut first_round_expanded);
2093
                // }
2094

2095
                if expander.changed || changed {
26,430✔
2096
                    // let source_id = self.sources.get_source_id(&module.name).unwrap();
2097

2098
                    let mut fully_expanded = expr;
804✔
2099
                    expand(&mut fully_expanded, &module.macro_map)?;
804✔
2100

2101
                    // Expanding the kernel with only these macros...
2102
                    let changed = expand_kernel_in_env_with_change(
2103
                        &mut fully_expanded,
804✔
2104
                        self.kernel.as_mut(),
804✔
2105
                        // We don't need to expand those here
2106
                        ModuleContainer::default(),
804✔
2107
                        &module.name.to_str().unwrap(),
804✔
2108
                        // &kernel_macros_in_scope,
2109
                    )?;
2110

2111
                    if changed {
17✔
2112
                        name_mangler.visit(&mut fully_expanded);
17✔
2113
                    }
2114

2115
                    // lifted_kernel_environments.insert(
2116
                    //     module_name.clone(),
2117
                    //     KernelDefMacroSpec {
2118
                    //         env: module_name,
2119
                    //         exported: None,
2120
                    //         name_mangler: name_mangler.clone(),
2121
                    //     },
2122
                    // );
2123

2124
                    // Ok(fully_expanded)
2125
                }
2126
                // else {
2127
                //     Ok(first_round_expanded)
2128
                // }
2129
            }
2130

2131
            // ast = ast
2132
            //     .into_iter()
2133
            //     .map(|x| {
2134
            //         // First expand the in scope macros
2135
            //         // These are macros
2136
            //         let mut expander = Expander::new(&in_scope_macros);
2137
            //         let mut first_round_expanded = expander.expand(x)?;
2138
            //         let mut changed = false;
2139

2140
            //         // dbg!(expander.changed);
2141

2142
            //         // (first_round_expanded, changed) = expand_kernel_in_env_with_allowed(
2143
            //         //     first_round_expanded,
2144
            //         //     self.kernel.as_mut(),
2145
            //         //     // We don't need to expand those here
2146
            //         //     ModuleContainer::default(),
2147
            //         //     module.name.to_str().unwrap().to_string(),
2148
            //         //     &kernel_macros_in_scope,
2149
            //         // )?;
2150

2151
            //         // If the kernel expander expanded into something - go ahead
2152
            //         // and expand all of the macros in this
2153
            //         // if changed || expander.changed {
2154
            //         // Expand here?
2155
            //         // first_round_expanded = expand(first_round_expanded, &module.macro_map)?;
2156

2157
            //         // Probably don't need this
2158
            //         // (first_round_expanded, changed) = expand_kernel_in_env_with_change(
2159
            //         //     first_round_expanded,
2160
            //         //     self.kernel.as_mut(),
2161
            //         //     ModuleContainer::default(),
2162
            //         //     module.name.to_str().unwrap().to_string(),
2163
            //         // )?;
2164

2165
            //         // name_mangler.visit(&mut first_round_expanded);
2166
            //         // }
2167

2168
            //         if expander.changed || changed {
2169
            //             // let source_id = self.sources.get_source_id(&module.name).unwrap();
2170

2171
            //             let mut fully_expanded = first_round_expanded;
2172
            //             expand(&mut fully_expanded, &module.macro_map)?;
2173

2174
            //             let module_name = module.name.to_str().unwrap().to_string();
2175

2176
            //             // Expanding the kernel with only these macros...
2177
            //             let changed = expand_kernel_in_env_with_change(
2178
            //                 &mut fully_expanded,
2179
            //                 self.kernel.as_mut(),
2180
            //                 // We don't need to expand those here
2181
            //                 ModuleContainer::default(),
2182
            //                 module_name.clone(),
2183
            //                 // &kernel_macros_in_scope,
2184
            //             )?;
2185

2186
            //             if changed {
2187
            //                 name_mangler.visit(&mut fully_expanded);
2188
            //             }
2189

2190
            //             // lifted_kernel_environments.insert(
2191
            //             //     module_name.clone(),
2192
            //             //     KernelDefMacroSpec {
2193
            //             //         env: module_name,
2194
            //             //         exported: None,
2195
            //             //         name_mangler: name_mangler.clone(),
2196
            //             //     },
2197
            //             // );
2198

2199
            //             Ok(fully_expanded)
2200
            //         } else {
2201
            //             Ok(first_round_expanded)
2202
            //         }
2203
            //     })
2204
            //     .collect::<Result<_>>()?;
2205

2206
            for expr in provides.iter_mut() {
2,771✔
2207
                // First expand the in scope macros
2208
                // These are macros
2209
                let mut expander = Expander::new(&in_scope_macros);
1,378✔
2210
                expander.expand(expr)?;
1,378✔
2211

2212
                if expander.changed {
1,378✔
2213
                    expand(expr, &module.macro_map)?
163✔
2214
                }
2215
                // else {
2216
                //     Ok(first_round_expanded)
2217
                // }
2218
            }
2219

2220
            // provides = provides
2221
            //     .into_iter()
2222
            //     .map(|x| {
2223
            //         // First expand the in scope macros
2224
            //         // These are macros
2225
            //         let mut expander = Expander::new(&in_scope_macros);
2226
            //         let first_round_expanded = expander.expand(x)?;
2227

2228
            //         expander.expand(&mut expr);
2229

2230
            //         if expander.changed {
2231
            //             expand(first_round_expanded, &module.macro_map)
2232
            //         } else {
2233
            //             Ok(first_round_expanded)
2234
            //         }
2235
            //     })
2236
            //     .collect::<Result<_>>()?;
2237
        }
2238

2239
        // let requires_before = self.require_objects.len();
2240

2241
        // self.collect_requires()?;
2242

2243
        // if self.require_objects.len() > requires_before {
2244
        //     println!("EXPANDED INTO A REQUIRE");
2245
        // }
2246

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

2250
        for expr in &mut ast {
30,371✔
NEW
2251
            lower_entire_ast(expr)?;
×
2252
            FlattenBegin::flatten(expr);
14,672✔
2253
        }
2254

2255
        // TODO: @Matt - fix this hack
2256
        {
2257
            self.source_ast = ast;
1,027✔
2258
            self.provides = provides;
1,027✔
2259

2260
            // println!("Collecting provides again:");
2261
            // println!("{}", self.source_ast);
2262
            // self.source_ast.pretty_print();
2263
            self.collect_provides()?;
1,027✔
2264

2265
            provides = std::mem::take(&mut self.provides);
1,027✔
2266
            ast = std::mem::take(&mut self.source_ast);
1,027✔
2267
        }
2268

2269
        // Put the mangled asts at the top
2270
        // then include the ast there
2271
        // mangled_asts.append(&mut ast);
2272

UNCOV
2273
        for expr in mangled_asts.iter_mut() {
×
UNCOV
2274
            lower_entire_ast(expr)?;
×
2275

UNCOV
2276
            FlattenBegin::flatten(expr);
×
2277
        }
2278

2279
        mangled_asts.append(&mut ast);
1,027✔
2280

2281
        // mangled_asts = mangled_asts
2282
        //     .into_iter()
2283
        //     .map(lower_entire_ast)
2284
        //     // We want this to at least be flattened for querying later
2285
        //     .map(|x| {
2286
        //         x.map(|mut o| {
2287
        //             FlattenBegin::flatten(&mut o);
2288
        //             o
2289
        //         })
2290
        //     })
2291
        //     .collect::<std::result::Result<_, ParseError>>()?;
2292

2293
        // Take ast, expand with self modules, then expand with each of the require for-syntaxes
2294
        // Then mangle the require-for-syntax, include the mangled directly in the ast
2295

2296
        // @Matt: 11/15/2024
2297
        // Try collecting the provides again?
2298

2299
        // TODO: Come back here - we're going to need to figure out the require objects
2300
        let mut module = CompiledModule::new(
2301
            self.name.clone(),
1,027✔
2302
            provides,
1,027✔
2303
            requires,
1,027✔
2304
            self.provides_for_syntax
1,027✔
2305
                .iter()
1,027✔
2306
                .map(|x| *x.atom_identifier().unwrap())
2,731✔
2307
                .collect(),
1,027✔
2308
            std::mem::take(&mut self.macro_map),
1,027✔
2309
            mangled_asts,
1,027✔
2310
        );
2311

2312
        module.set_emitted(true);
1,027✔
2313

2314
        // println!(
2315
        //     "-------------- Emitting module: {:?} ----------------------",
2316
        //     self.name
2317
        // );
2318

2319
        let result = module.to_top_level_module(self.compiled_modules, self.global_macro_map)?;
2,054✔
2320

2321
        // println!("{}", result.to_pretty(60));
2322

2323
        // println!("------------------ Finish ----------------------------------");
2324

2325
        // let mut analysis = Analysis::from_exprs(&[result]);
2326

2327
        // let mut semantic = SemanticAnalysis::from_analysis(&mut result, analysis);
2328

2329
        // // This is definitely broken still
2330
        // semantic
2331
        //     .remove_unused_globals_with_prefix("mangler");
2332

2333
        // log::debug!(target: "requires", "Adding compiled module: {:?}", self.name);
2334
        // println!("Adding compiled module: {:?}", self.name);
2335
        // for (key, smacro) in module.macro_map.iter() {
2336
        //     println!("{}", key.resolve());
2337
        //     for expr in smacro.exprs() {
2338
        //         println!("{}", expr);
2339
        //     }
2340
        // }
2341

2342
        self.compiled_modules.insert(self.name.clone(), module);
1,027✔
2343

2344
        Ok(result)
1,027✔
2345
    }
2346

2347
    fn extract_macro_defs(&mut self) -> Result<()> {
1,822✔
2348
        // Probably don't have more than 128 macros in a module, but who knows?
2349
        // let mut macro_indices = SmallVec::<[usize; 128]>::new();
2350

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

2353
        let mut error = None;
1,822✔
2354

2355
        self.source_ast.retain_mut(|expr| {
25,582✔
2356
            if let ExprKind::Macro(_) = expr {
23,760✔
2357
                // Replace with dummy begin value so we don't have to copy
2358
                // everything other for every macro definition
2359
                let mut taken_expr = ExprKind::Begin(Box::new(Begin::new(
1,488✔
2360
                    Vec::new(),
1,488✔
2361
                    SyntaxObject::default(TokenType::Begin),
1,488✔
2362
                )));
2363

2364
                std::mem::swap(expr, &mut taken_expr);
1,488✔
2365

2366
                if let ExprKind::Macro(m) = taken_expr {
1,488✔
2367
                    match SteelMacro::parse_from_ast_macro(m) {
×
2368
                        Ok(generated_macro) => {
1,488✔
2369
                            let name = generated_macro.name();
1,488✔
2370
                            self.macro_map.insert(*name, generated_macro);
1,488✔
2371
                        }
2372
                        Err(e) => {
×
2373
                            if error.is_none() {
×
2374
                                error = Some(e);
×
2375
                            }
2376
                            // error = Some(e);
2377
                            return false;
×
2378
                        }
2379
                    }
2380
                } else {
2381
                    unreachable!();
×
2382
                }
2383

2384
                return false;
1,488✔
2385
            }
2386

2387
            true
22,272✔
2388
        });
2389

2390
        if let Some(e) = error {
1,822✔
2391
            return Err(e);
×
2392
        }
2393

2394
        Ok(())
1,822✔
2395
    }
2396

2397
    // Takes out the (for-syntax) forms from the provides
2398
    fn filter_out_for_syntax_provides(&mut self, exprs: Vec<ExprKind>) -> Result<Vec<ExprKind>> {
1,701✔
2399
        let mut normal_provides = Vec::new();
1,701✔
2400

2401
        for expr in exprs {
29,061✔
2402
            match &expr {
×
2403
                ExprKind::Atom(_) => {
12,506✔
2404
                    normal_provides.push(expr);
12,506✔
2405
                }
2406
                ExprKind::List(l) => {
1,174✔
2407
                    if let Some(for_syntax) = l.first_ident() {
1,174✔
2408
                        match *for_syntax {
497✔
2409
                            x if x == *FOR_SYNTAX => {
1,354✔
2410
                                if l.args.len() != 2 {
677✔
2411
                                    stop!(ArityMismatch => "provide expects a single identifier in the (for-syntax <ident>)"; l.location)
×
2412
                                }
2413

2414
                                // Collect the for syntax expressions
2415
                                // TODO -> remove this clone
2416
                                self.provides_for_syntax.push(l.args[1].clone());
677✔
2417
                            }
2418
                            x if x == *REQUIRE_IDENT_SPEC => {
501✔
2419
                                normal_provides.push(expr);
2✔
2420
                            }
2421
                            // x if x == *CONTRACT_OUT || x == *REQUIRE_IDENT_SPEC => {
2422
                            //     normal_provides.push(expr);
2423
                            // }
2424
                            _ => {
495✔
2425
                                normal_provides.push(expr);
495✔
2426
                                // stop!(TypeMismatch => "provide expects either an identifier, (for-syntax <ident>), or (contract/out ...)")
2427
                            }
2428
                        }
2429
                    } else {
2430
                        stop!(TypeMismatch => "provide expects either an identifier or a (for-syntax <ident>)"; l.location)
×
2431
                    }
2432
                }
2433
                _ => {
×
2434
                    stop!(TypeMismatch => "provide expects either a (for-syntax <ident>) or an ident"; opt expr.span())
×
2435
                }
2436
            }
2437
        }
2438

2439
        Ok(normal_provides)
1,701✔
2440
    }
2441

2442
    // TODO -> collect (provide (for-syntax ...))
2443
    // I think these will already be collected for the macro, however I think for syntax should be found earlier
2444
    // Otherwise the macro expansion will not be able to understand it
2445
    fn collect_provides(&mut self) -> Result<()> {
3,644✔
2446
        // let now = std::time::Instant::now();
2447

2448
        let mut non_provides = Vec::with_capacity(self.source_ast.len());
3,644✔
2449
        let exprs = std::mem::take(&mut self.source_ast);
3,644✔
2450

2451
        fn walk(
6,215✔
2452
            module_builder: &mut ModuleBuilder,
2453
            exprs: Vec<ExprKind>,
2454
            non_provides: &mut Vec<ExprKind>,
2455
        ) -> Result<()> {
2456
            for mut expr in exprs {
472,971✔
2457
                match &mut expr {
×
2458
                    ExprKind::List(l) => {
32,276✔
2459
                        if let Some(provide) = l.first_ident() {
11,383✔
2460
                            if *provide == *PROVIDE {
×
2461
                                if l.len() == 1 {
1,701✔
2462
                                    stop!(Generic => "provide expects at least one identifier to provide"; l.location);
×
2463
                                }
2464

2465
                                // Swap out the value inside the list
2466
                                let args = std::mem::take(&mut l.args);
1,701✔
2467

2468
                                let filtered =
1,701✔
2469
                                    module_builder.filter_out_for_syntax_provides(args)?;
×
2470

2471
                                l.args = filtered;
1,701✔
2472

2473
                                module_builder.provides.push(expr);
1,701✔
2474

2475
                                continue;
1,701✔
2476
                            }
2477
                        }
2478
                    }
2479
                    ExprKind::Begin(b) => {
2,571✔
2480
                        let exprs = std::mem::take(&mut b.exprs);
2,571✔
2481

2482
                        // Reserve capacity for these to be moved to the top level
2483
                        non_provides.reserve(exprs.len());
2,571✔
2484

2485
                        walk(module_builder, exprs, non_provides)?;
2,571✔
2486
                    }
2487
                    _ => {}
198,531✔
2488
                }
2489

2490
                non_provides.push(expr);
231,677✔
2491
            }
2492

2493
            Ok(())
6,215✔
2494
        }
2495

2496
        walk(self, exprs, &mut non_provides)?;
3,644✔
2497

2498
        self.source_ast = non_provides;
3,644✔
2499

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

2502
        Ok(())
3,644✔
2503
    }
2504

2505
    fn parse_require_object(
2,575✔
2506
        &mut self,
2507
        home: &Option<PathBuf>,
2508
        r: &crate::parser::ast::Require,
2509
        atom: &ExprKind,
2510
    ) -> Result<RequireObject> {
2511
        let mut object = RequireObjectBuilder::default();
2,575✔
2512

2513
        // Set the span so we can pass through an error if the
2514
        // module is not found
2515
        object.span = r.location.span;
2,575✔
2516

2517
        self.parse_require_object_inner(home, r, atom, &mut object)
2,575✔
2518
            .and_then(|_| object.build())
7,725✔
2519
    }
2520

2521
    // TODO: Recursively crunch the requires to gather up the necessary information
2522
    fn parse_require_object_inner(
2,580✔
2523
        &mut self,
2524
        home: &Option<PathBuf>,
2525
        r: &crate::parser::ast::Require,
2526
        atom: &ExprKind,
2527
        require_object: &mut RequireObjectBuilder,
2528
    ) -> Result<()> {
2529
        match atom {
1,809✔
2530
            ExprKind::Atom(Atom {
×
2531
                syn:
×
2532
                    SyntaxObject {
×
2533
                        ty: TokenType::StringLiteral(s),
1,809✔
2534
                        span,
1,809✔
2535
                        ..
1,809✔
2536
                    },
2537
            }) => {
1,809✔
2538
                if require_object.path.is_some() {
1,809✔
2539
                    stop!(Generic => "require object only expects one path!")
×
2540
                }
2541

2542
                // Try this?
2543
                if let Some(lib) = BUILT_INS.into_iter().cloned().find(|x| x.0 == s.as_str()) {
16,930✔
2544
                    // self.built_ins.push(PathBuf::from(lib.0));
2545

2546
                    require_object.path = Some(PathOrBuiltIn::BuiltIn(lib.0.into()));
1,737✔
2547

2548
                    return Ok(());
1,737✔
2549
                    // continue;
2550
                }
2551

2552
                if self.custom_builtins.contains_key(s.as_str()) {
72✔
2553
                    require_object.path =
×
2554
                        Some(PathOrBuiltIn::BuiltIn(s.clone().to_string().into()));
×
2555

2556
                    return Ok(());
×
2557
                }
2558

2559
                if cfg!(target_arch = "wasm32") {
2560
                    stop!(Generic => "requiring modules is not supported for wasm");
×
2561
                }
2562

2563
                let mut current = self.name.clone();
72✔
2564
                if current.is_file() {
115✔
2565
                    current.pop();
43✔
2566
                }
2567
                current.push(PathBuf::from(s.as_str()));
×
2568

2569
                // // If the path exists on its own, we can continue
2570
                // // But theres the case where we're searching for a module on the STEEL_HOME
2571
                if !current.exists() {
×
2572
                    if let Some(mut home) = home.clone() {
42✔
2573
                        home.push(PathBuf::from(s.as_str()));
×
2574
                        current = home;
×
2575

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

2578
                        if !current.exists() {
21✔
2579
                            for dir in self.search_dirs {
×
2580
                                let mut dir = dir.clone();
×
2581
                                dir.push(s.as_str());
×
2582

2583
                                if dir.exists() {
×
2584
                                    current = dir;
×
2585
                                    break;
×
2586
                                }
2587
                            }
2588
                        }
2589
                    } else {
2590
                        // TODO: Check if this module exists in STEEL_HOME first. If it does, we'll take that as our candidate
2591
                        // and then continue on to the final module resolution part.
2592
                        //
2593
                        // If it doesn't exist, we should iterate through the search directories and attempt to find
2594
                        // a matching path there.
2595

2596
                        for dir in self.search_dirs {
×
2597
                            let mut dir = dir.clone();
×
2598
                            dir.push(s.as_str());
×
2599

2600
                            if dir.exists() {
×
2601
                                current = dir;
×
2602
                                break;
×
2603
                            }
2604
                        }
2605

2606
                        stop!(Generic => format!("Module not found: {:?} with STEEL_HOME: {:?}", current, home); *span)
×
2607
                    }
2608
                }
2609

2610
                // Get the absolute path and store that
2611
                // self.requires.push(current)
2612

2613
                let current = try_canonicalize(current);
72✔
2614

2615
                require_object.path = Some(PathOrBuiltIn::Path(current));
72✔
2616
            }
2617

2618
            // TODO: Requires with qualifiers, that aren't just for-syntax
2619
            // Perhaps like:
2620
            // (with-prefix <xyz>)
2621
            ExprKind::List(l) => {
771✔
2622
                match l.first_ident() {
771✔
2623
                    Some(x) if *x == *ONLY_IN => {
779✔
2624
                        if l.args.len() < 2 {
4✔
2625
                            stop!(BadSyntax => "only-in expects a require-spec and optionally a list of ids to bind (maybe renamed)"; l.location);
×
2626
                        }
2627

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

2630
                        for remaining in &l.args[2..] {
16✔
2631
                            match remaining {
12✔
2632
                                ExprKind::Atom(_) => {
10✔
2633
                                    require_object
10✔
2634
                                        .idents_to_import
10✔
2635
                                        .push(MaybeRenamed::Normal(remaining.clone()));
10✔
2636
                                }
2637
                                ExprKind::List(l) => {
2✔
2638
                                    if l.len() != 2 {
2✔
2639
                                        stop!(BadSyntax => "Expected a pair when renaming required identifiers");
×
2640
                                    }
2641

2642
                                    let from = &l.args[0];
2✔
2643
                                    let to = &l.args[1];
2✔
2644

2645
                                    if from.atom_identifier().is_none()
2✔
2646
                                        || to.atom_identifier().is_none()
2✔
2647
                                    {
2648
                                        stop!(BadSyntax => "only-in expected identifiers to rename");
×
2649
                                    }
2650

2651
                                    // (<from> <to>)
2652
                                    require_object
2✔
2653
                                        .idents_to_import
2✔
2654
                                        .push(MaybeRenamed::Renamed(from.clone(), to.clone()));
2✔
2655
                                }
2656
                                _ => {
×
2657
                                    stop!(BadSyntax => "unexpected syntax in only-in form during module requires")
×
2658
                                }
2659
                            }
2660
                        }
2661
                    }
2662

2663
                    Some(x) if *x == *PREFIX_IN => {
769✔
2664
                        if l.args.len() != 3 {
1✔
2665
                            stop!(BadSyntax => "prefix-in expects a prefix to prefix a given file or module"; l.location);
×
2666
                        }
2667

2668
                        let prefix = &l.args[1];
1✔
2669

2670
                        if let Some(prefix) = prefix.atom_identifier() {
1✔
2671
                            match &mut require_object.prefix {
×
2672
                                Some(existing_prefix) => {
×
2673
                                    // Append the new symbol to the existing prefix
2674
                                    existing_prefix.push_str(prefix.resolve());
×
2675
                                }
2676
                                None => {
1✔
2677
                                    require_object.prefix = Some(prefix.resolve().to_string());
1✔
2678
                                }
2679
                            }
2680

2681
                            self.parse_require_object_inner(home, r, &l.args[2], require_object)?;
×
2682
                        } else {
2683
                            stop!(TypeMismatch => "prefix-in expects an identifier to use for the prefix"; opt prefix.span());
×
2684
                        }
2685
                    }
2686

2687
                    Some(x) if *x == *FOR_SYNTAX => {
1,532✔
2688
                        // We're expecting something like (for-syntax "foo")
2689
                        if l.args.len() != 2 {
×
2690
                            stop!(BadSyntax => "for-syntax expects one string literal referring to a file or module"; l.location);
×
2691
                        }
2692

2693
                        let mod_name = &l.args[1];
766✔
2694
                        if let Some(path) = mod_name.string_literal() {
766✔
2695
                            if let Some(lib) = BUILT_INS.iter().find(|x| x.0 == path) {
6,924✔
2696
                                // self.built_ins.push(PathBuf::from(lib.0));
2697

2698
                                require_object.path = Some(PathOrBuiltIn::BuiltIn(lib.0.into()));
756✔
2699
                                require_object.for_syntax = true;
756✔
2700

2701
                                return Ok(());
756✔
2702
                                // continue;
2703
                            } else if self.custom_builtins.contains_key(path) {
10✔
2704
                                require_object.path =
×
2705
                                    Some(PathOrBuiltIn::BuiltIn(Cow::Owned(path.to_string())));
×
2706
                                require_object.for_syntax = true;
×
2707

2708
                                return Ok(());
×
2709
                            } else {
2710
                                let mut current = self.name.clone();
10✔
2711
                                if current.is_file() {
20✔
2712
                                    current.pop();
10✔
2713
                                }
2714
                                current.push(PathBuf::from(path));
×
2715

2716
                                if !current.exists() {
×
2717
                                    if let Some(mut home) = home.clone() {
10✔
2718
                                        home.push(PathBuf::from(path));
×
2719
                                        current = home;
×
2720

2721
                                        if !current.exists() {
×
2722
                                            for dir in self.search_dirs {
×
2723
                                                let mut dir = dir.clone();
×
2724
                                                dir.push(path);
×
2725

2726
                                                if dir.exists() {
×
2727
                                                    current = dir;
×
2728
                                                    break;
×
2729
                                                }
2730
                                            }
2731
                                        }
2732

2733
                                        log::info!("Searching STEEL_HOME for {:?}", current);
5✔
2734
                                    } else {
2735
                                        for dir in self.search_dirs {
×
2736
                                            let mut dir = dir.clone();
×
2737
                                            dir.push(path);
×
2738

2739
                                            if dir.exists() {
×
2740
                                                current = dir;
×
2741
                                                break;
×
2742
                                            }
2743
                                        }
2744

2745
                                        stop!(Generic => format!("Module not found: {:?}", current); mod_name.span().unwrap())
×
2746
                                    }
2747
                                }
2748

2749
                                require_object.for_syntax = true;
10✔
2750
                                let current = try_canonicalize(current);
10✔
2751
                                require_object.path = Some(PathOrBuiltIn::Path(current));
10✔
2752
                            }
2753
                        } else {
2754
                            stop!(BadSyntax => "for-syntax expects a string literal referring to a file or module"; opt mod_name.span());
×
2755
                        }
2756
                    }
2757
                    _ => {
×
2758
                        stop!(BadSyntax => "require accepts either a string literal, a for-syntax expression or an only-in expression"; l.location)
×
2759
                    }
2760
                }
2761
            }
2762

2763
            unknown => {
×
2764
                stop!(Generic => format!("require object expected a string literal referring to a file/module, found: {}", unknown); opt atom.span())
×
2765
            }
2766
        }
2767

2768
        Ok(())
87✔
2769
    }
2770

2771
    fn collect_requires(&mut self) -> Result<()> {
1,822✔
2772
        // unimplemented!()
2773

2774
        let mut exprs_without_requires = Vec::new();
1,822✔
2775
        let exprs = std::mem::take(&mut self.source_ast);
1,822✔
2776

2777
        let home = STEEL_HOME
1,822✔
2778
            .clone()
2779
            .map(|x| {
3,644✔
2780
                // TODO: Fix this - try to hack in a root drive
2781
                // for windows if a unix path is provided
2782
                if cfg!(target_os = "windows") {
1,822✔
2783
                    let mut result = x.trim_start_matches("/").to_string();
×
2784

2785
                    let mut iter = result.chars();
×
2786
                    iter.next();
×
2787
                    if matches!(iter.next(), Some(':')) {
×
2788
                        return PathBuf::from(result);
×
2789
                    }
2790

2791
                    result.insert(1, ':');
×
2792
                    return PathBuf::from(result);
×
2793
                }
2794

2795
                PathBuf::from(x)
1,822✔
2796
            })
2797
            .map(|mut x| {
3,644✔
2798
                x.push("cogs");
1,822✔
2799

2800
                x
1,822✔
2801
            });
2802

2803
        fn walk(
1,822✔
2804
            module_builder: &mut ModuleBuilder,
2805
            home: &Option<PathBuf>,
2806
            exprs_without_requires: &mut Vec<ExprKind>,
2807
            exprs: Vec<ExprKind>,
2808
        ) -> Result<()> {
2809
            for expr in exprs {
57,212✔
2810
                match expr {
×
2811
                    // Include require/for-syntax here
2812
                    // This way we have some understanding of what dependencies a file has
2813
                    ExprKind::Require(r) => {
2,238✔
2814
                        for atom in &r.modules {
7,388✔
2815
                            // TODO: Consider making this not a reference for r
2816
                            let require_object =
2,575✔
2817
                                module_builder.parse_require_object(&home, &r, atom)?;
×
2818

2819
                            module_builder.require_objects.push(require_object);
2,575✔
2820
                        }
2821
                    }
2822
                    ExprKind::Begin(b) => {
×
2823
                        walk(module_builder, home, exprs_without_requires, b.exprs)?
×
2824
                    }
2825
                    _ => exprs_without_requires.push(expr),
25,457✔
2826
                }
2827
            }
2828

2829
            Ok(())
1,822✔
2830
        }
2831

2832
        walk(self, &home, &mut exprs_without_requires, exprs)?;
1,822✔
2833

2834
        self.source_ast = exprs_without_requires;
1,822✔
2835
        Ok(())
1,822✔
2836
    }
2837

2838
    #[allow(clippy::too_many_arguments)]
2839
    fn new_built_in(
973✔
2840
        name: PathBuf,
2841
        input: Cow<'static, str>,
2842
        compiled_modules: &'a mut FxHashMap<PathBuf, CompiledModule>,
2843
        visited: &'a mut FxHashSet<PathBuf>,
2844
        file_metadata: &'a mut FxHashMap<PathBuf, SystemTime>,
2845
        sources: &'a mut Sources,
2846
        kernel: &'a mut Option<Kernel>,
2847
        builtin_modules: ModuleContainer,
2848
        global_macro_map: &'a FxHashMap<InternedString, SteelMacro>,
2849
        custom_builtins: &'a HashMap<String, String>,
2850
    ) -> Result<Self> {
2851
        ModuleBuilder::raw(
2852
            name,
973✔
2853
            compiled_modules,
973✔
2854
            visited,
973✔
2855
            file_metadata,
973✔
2856
            sources,
973✔
2857
            kernel,
973✔
2858
            builtin_modules,
973✔
2859
            global_macro_map,
973✔
2860
            custom_builtins,
973✔
2861
            &[],
973✔
2862
        )
2863
        .parse_builtin(input)
973✔
2864
    }
2865

2866
    fn new_from_path(
54✔
2867
        name: PathBuf,
2868
        compiled_modules: &'a mut FxHashMap<PathBuf, CompiledModule>,
2869
        visited: &'a mut FxHashSet<PathBuf>,
2870
        file_metadata: &'a mut FxHashMap<PathBuf, SystemTime>,
2871
        sources: &'a mut Sources,
2872
        kernel: &'a mut Option<Kernel>,
2873
        builtin_modules: ModuleContainer,
2874
        global_macro_map: &'a FxHashMap<InternedString, SteelMacro>,
2875
        custom_builtins: &'a HashMap<String, String>,
2876
        search_dirs: &'a [PathBuf],
2877
    ) -> Result<Self> {
2878
        ModuleBuilder::raw(
2879
            name,
54✔
2880
            compiled_modules,
54✔
2881
            visited,
54✔
2882
            file_metadata,
54✔
2883
            sources,
54✔
2884
            kernel,
54✔
2885
            builtin_modules,
54✔
2886
            global_macro_map,
54✔
2887
            custom_builtins,
54✔
2888
            search_dirs,
54✔
2889
        )
2890
        .parse_from_path()
2891
    }
2892

2893
    fn raw(
1,027✔
2894
        name: PathBuf,
2895
        compiled_modules: &'a mut FxHashMap<PathBuf, CompiledModule>,
2896
        visited: &'a mut FxHashSet<PathBuf>,
2897
        file_metadata: &'a mut FxHashMap<PathBuf, SystemTime>,
2898
        sources: &'a mut Sources,
2899
        kernel: &'a mut Option<Kernel>,
2900
        builtin_modules: ModuleContainer,
2901
        global_macro_map: &'a FxHashMap<InternedString, SteelMacro>,
2902
        custom_builtins: &'a HashMap<String, String>,
2903
        search_dirs: &'a [PathBuf],
2904
    ) -> Self {
2905
        // println!("New module found: {:?}", name);
2906

2907
        let name = try_canonicalize(name);
1,027✔
2908

2909
        ModuleBuilder {
2910
            name,
2911
            main: false,
2912
            source_ast: Vec::new(),
1,027✔
2913
            // TODO: This used to be empty
2914
            macro_map: default_prelude_macros(),
1,027✔
2915
            // macro_map: global_macro_map.clone(),
2916
            require_objects: Vec::new(),
1,027✔
2917
            provides: Vec::new(),
1,027✔
2918
            provides_for_syntax: Vec::new(),
1,027✔
2919
            compiled_modules,
2920
            visited,
2921
            file_metadata,
2922
            sources,
2923
            kernel,
2924
            builtin_modules,
2925
            global_macro_map,
2926
            custom_builtins,
2927
            search_dirs,
2928
        }
2929
    }
2930

2931
    fn parse_builtin(mut self, input: Cow<'static, str>) -> Result<Self> {
973✔
2932
        #[cfg(feature = "profiling")]
2933
        let now = std::time::Instant::now();
973✔
2934

2935
        let id = self
973✔
2936
            .sources
973✔
2937
            .add_source(input.clone(), Some(self.name.clone()));
973✔
2938

2939
        let parsed = Parser::new_from_source(&input, self.name.clone(), Some(id))
1,946✔
2940
            .without_lowering()
2941
            .map(|x| x.and_then(lower_macro_and_require_definitions))
20,267✔
2942
            .collect::<std::result::Result<Vec<_>, ParseError>>()?;
2943

2944
        self.source_ast = parsed;
×
2945

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

2949
        // self.source_ast.pretty_print();
2950

2951
        Ok(self)
×
2952
    }
2953

2954
    fn parse_from_path(mut self) -> Result<Self> {
54✔
2955
        log::info!("Opening: {:?}", self.name);
54✔
2956

2957
        let mut file = std::fs::File::open(&self.name).map_err(|err| {
108✔
2958
            let mut err = crate::SteelErr::from(err);
×
2959
            err.prepend_message(&format!("Attempting to load module from: {:?}", self.name));
×
2960
            err
×
2961
        })?;
2962
        self.file_metadata
×
2963
            .insert(self.name.clone(), file.metadata()?.modified()?);
54✔
2964

2965
        // TODO: DEFAULT MODULE LOADER PREFIX
2966
        let mut exprs = String::new();
54✔
2967

2968
        // TODO: Don't do this - get the source from the cache?
2969
        // let mut exprs = PRELUDE_STRING.to_string();
2970

2971
        let mut expressions = Parser::new(&PRELUDE_STRING, SourceId::none())
54✔
2972
            .without_lowering()
2973
            .map(|x| x.and_then(lower_macro_and_require_definitions))
432✔
2974
            .collect::<std::result::Result<Vec<_>, ParseError>>()?;
2975

2976
        // let expressions = Parser::new_from_source(, , )
2977

2978
        // Add the modules here:
2979

2980
        // exprs.push_str(ALL_MODULES);
2981

2982
        file.read_to_string(&mut exprs)?;
×
2983

2984
        let id = self.sources.add_source(exprs, Some(self.name.clone()));
54✔
2985

2986
        {
2987
            // Fetch the exprs after adding them to the sources
2988
            // We did _just_ add it, so its fine to unwrap
2989
            let guard = self.sources.sources.lock().unwrap();
54✔
2990

2991
            let exprs = guard.get(id).unwrap();
54✔
2992

2993
            let mut parsed = Parser::new_from_source(&exprs, self.name.clone(), Some(id))
54✔
2994
                .without_lowering()
2995
                .map(|x| x.and_then(lower_macro_and_require_definitions))
788✔
2996
                .collect::<std::result::Result<Vec<_>, ParseError>>()?;
2997

2998
            expressions.append(&mut parsed);
54✔
2999

3000
            self.source_ast = expressions;
54✔
3001
        }
3002

3003
        Ok(self)
54✔
3004
    }
3005
}
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