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

mattwparas / steel / 11687386167

05 Nov 2024 03:30PM UTC coverage: 47.373% (-0.1%) from 47.476%
11687386167

push

github

web-flow
analysis: don't do analysis for empty list (#286)

1 of 2 new or added lines in 1 file covered. (50.0%)

41 existing lines in 13 files now uncovered.

12433 of 26245 relevant lines covered (47.37%)

490139.96 hits per line

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

70.07
/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 fxhash::{FxHashMap, FxHashSet};
22
use once_cell::sync::Lazy;
23
// use smallvec::SmallVec;
24
use steel_parser::{ast::PROTO_HASH_GET, expr_list, parser::SourceId, span::Span};
25

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

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

36
use std::time::SystemTime;
37

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

40
use log::log_enabled;
41

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

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

57
        let e = $e;
58

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

62
        e
63
    }};
64
}
65

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

UNCOV
183
    pub fn modules(&self) -> &FxHashMap<PathBuf, CompiledModule> {
×
UNCOV
184
        &self.compiled_modules
×
185
    }
186

UNCOV
187
    pub fn modules_mut(&mut self) -> &mut FxHashMap<PathBuf, CompiledModule> {
×
UNCOV
188
        &mut self.compiled_modules
×
189
    }
190

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

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

206
        self.visited.clear();
×
207

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

223
        module_builder.compile()?;
×
224

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

227
        Ok(())
×
228
    }
229

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

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

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

267
        let mut module_statements = module_builder.compile()?;
776✔
268

269
        // for expr in module_builder.source_ast.iter_mut() {
270
        //     expand(expr, global_macro_map)?;
271
        // }
272

273
        // let mut ast = module_builder.source_ast;
274

275
        {
276
            // module_builder.source_ast = ast;
277
            module_builder.collect_provides()?;
×
278

279
            // ast = std::mem::take(&mut module_builder.source_ast);
280
        }
281

282
        let mut ast = module_builder.source_ast;
776✔
283

284
        let mut require_defines = Vec::new();
776✔
285

286
        // let mut mangled_prefixes = module_builder
287
        //     .require_objects
288
        //     .iter()
289
        //     .filter(|x| !x.for_syntax)
290
        //     .map(|x| {
291
        //         "mangler".to_string() + x.path.get_path().to_str().unwrap() + MANGLER_SEPARATOR
292
        //     })
293
        //     .collect::<Vec<_>>();
294

295
        let mut explicit_requires = HashMap::new();
776✔
296

297
        for require_object in &module_builder.require_objects {
3,096✔
298
            let path = require_object.path.get_path();
299
            explicit_requires.clear();
300

301
            // If there _are_ explicit identifiers to import, limit what we import to what
302
            // is in the set
303
            for ident in &require_object.idents_to_import {
1,178✔
304
                match ident {
305
                    MaybeRenamed::Normal(i) => {
7✔
306
                        explicit_requires.insert(i.atom_identifier().unwrap().clone(), None);
7✔
307
                    }
308

309
                    MaybeRenamed::Renamed(from, to) => {
2✔
310
                        explicit_requires.insert(
2✔
311
                            from.atom_identifier().unwrap().clone(),
2✔
312
                            Some(to.atom_identifier().unwrap().clone()),
2✔
313
                        );
314
                    }
315
                }
316
            }
317

318
            // println!("{:?}", path);
319
            let module = if let Some(module) = module_builder.compiled_modules.get(path.as_ref()) {
2,320✔
320
                module
321
            } else {
322
                // log::debug!(target: "modules", "No provides found for module, skipping: {:?}", path);
323

324
                continue;
×
325
            };
326

327
            for provide_expr in &module.provides {
6,022✔
328
                // For whatever reason, the value coming into module.provides is an expression like: (provide expr...)
329
                for provide in &provide_expr.list().unwrap().args[1..] {
14,017✔
330
                    // println!("{}", provide);
331

332
                    // println!("Top level provide handler");
333

334
                    // Would be nice if this could be handled by some macro expansion...
335
                    // See if contract/out
336

337
                    let other_module_prefix =
14,017✔
338
                        "mangler".to_string() + module.name.to_str().unwrap() + MANGLER_SEPARATOR;
14,017✔
339

340
                    // TODO: Expand the contract out into something we expect
341
                    // Otherwise, this is going to blow up
342
                    match provide {
14,017✔
343
                        ExprKind::List(l) => {
24✔
344
                            if let Some(qualifier) = l.first_ident() {
48✔
345
                                match *qualifier {
346
                                    x if x == *REQUIRE_IDENT_SPEC => {
48✔
347
                                        // Directly expand into define/contract, but with the value just being the hash get below
348

349
                                        // (bind/c contract name 'name)
350

351
                                        let name = l.args.get(1).unwrap();
24✔
352

353
                                        if !explicit_requires.is_empty()
24✔
354
                                            && !name
9✔
355
                                                .atom_identifier()
9✔
356
                                                .map(|x| explicit_requires.contains_key(x))
18✔
357
                                                .unwrap_or_default()
358
                                        {
359
                                            continue;
6✔
360
                                        }
361

362
                                        if module
18✔
363
                                            .macro_map
18✔
364
                                            .contains_key(name.atom_identifier().unwrap())
18✔
365
                                        {
366
                                            continue;
×
367
                                        }
368

369
                                        let hash_get = expr_list![
18✔
370
                                            ExprKind::atom(*PROTO_HASH_GET),
18✔
371
                                            ExprKind::atom(
18✔
372
                                                "__module-".to_string() + &other_module_prefix
18✔
373
                                            ),
374
                                            ExprKind::Quote(Box::new(Quote::new(
18✔
375
                                                name.clone(),
18✔
376
                                                SyntaxObject::default(TokenType::Quote)
18✔
377
                                            ))),
378
                                        ];
379

380
                                        let mut owned_name = name.clone();
18✔
381

382
                                        // If we have the alias listed, we should use it
383
                                        if !explicit_requires.is_empty() {
18✔
384
                                            if let Some(alias) = explicit_requires
3✔
385
                                                .get(name.atom_identifier().unwrap())
3✔
386
                                                .copied()
387
                                                .flatten()
388
                                            {
389
                                                *owned_name.atom_identifier_mut().unwrap() =
390
                                                    alias.clone();
391
                                            }
392
                                        }
393

394
                                        if let Some(prefix) = &require_object.prefix {
3✔
395
                                            if let Some(existing) = owned_name.atom_identifier_mut()
3✔
396
                                            {
397
                                                let mut prefixed_identifier = prefix.clone();
3✔
398
                                                prefixed_identifier.push_str(existing.resolve());
3✔
399

400
                                                // Update the existing identifier to point to a new one with the prefix applied
401
                                                *existing = prefixed_identifier.into();
3✔
402
                                            }
403
                                        }
404

405
                                        let define = ExprKind::Define(Box::new(Define::new(
18✔
406
                                            owned_name,
18✔
407
                                            hash_get,
18✔
408
                                            SyntaxObject::default(TokenType::Define),
18✔
409
                                        )));
410

411
                                        require_defines.push(define);
18✔
412
                                    }
413
                                    _ => {
414
                                        stop!(TypeMismatch => format!("provide expects either an identifier, (for-syntax <ident>), or (contract/out ...), found: {}", provide))
×
415
                                    }
416
                                }
417
                            } else {
418
                                stop!(TypeMismatch => "provide expects either an identifier or a (for-syntax <ident>)")
×
419
                            }
420
                        }
421
                        ExprKind::Atom(_) => {
422
                            if !explicit_requires.is_empty()
13,993✔
423
                                && !provide
27✔
424
                                    .atom_identifier()
27✔
425
                                    .map(|x| explicit_requires.contains_key(x))
54✔
426
                                    .unwrap_or_default()
427
                            {
428
                                continue;
21✔
429
                            }
430

431
                            if module
13,972✔
432
                                .macro_map
13,972✔
433
                                .contains_key(provide.atom_identifier().unwrap())
13,972✔
434
                            {
435
                                continue;
479✔
436
                            }
437

438
                            let hash_get = expr_list![
13,493✔
439
                                ExprKind::atom(*PROTO_HASH_GET),
13,493✔
440
                                ExprKind::atom("__module-".to_string() + &other_module_prefix),
13,493✔
441
                                ExprKind::Quote(Box::new(Quote::new(
13,493✔
442
                                    provide.clone(),
13,493✔
443
                                    SyntaxObject::default(TokenType::Quote)
13,493✔
444
                                ))),
445
                            ];
446

447
                            let mut owned_provide = provide.clone();
13,493✔
448

449
                            // If we have the alias listed, we should use it
450
                            if !explicit_requires.is_empty() {
13,493✔
451
                                if let Some(alias) = explicit_requires
8✔
452
                                    .get(provide.atom_identifier().unwrap())
6✔
453
                                    .copied()
454
                                    .flatten()
455
                                {
456
                                    *owned_provide.atom_identifier_mut().unwrap() = alias.clone();
457
                                }
458
                            }
459

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

464
                                if let Some(existing) = owned_provide.atom_identifier_mut() {
18✔
465
                                    let mut prefixed_identifier = prefix.clone();
9✔
466
                                    prefixed_identifier.push_str(existing.resolve());
9✔
467

468
                                    // Update the existing identifier to point to a new one with the prefix applied
469
                                    *existing = prefixed_identifier.into();
9✔
470
                                }
471
                            }
472

473
                            let define = ExprKind::Define(Box::new(Define::new(
13,493✔
474
                                owned_provide,
13,493✔
475
                                hash_get,
13,493✔
476
                                SyntaxObject::default(TokenType::Define),
13,493✔
477
                            )));
478

479
                            // println!("{}", define);
480

481
                            require_defines.push(define);
13,493✔
482
                        }
483
                        _ => {
484
                            stop!(TypeMismatch => "provide expression needs to either be a `contract/out` form or an identifier")
×
485
                        }
486
                    }
487
                }
488
            }
489
        }
490

491
        let mut mangled_asts = Vec::with_capacity(ast.len());
776✔
492

493
        // TODO: Move this to the lower level as well
494
        // It seems we're only doing this expansion at the top level, but we _should_ do this at the lower level as well
495
        for require_object in module_builder.require_objects.iter()
1,936✔
496
        // .filter(|x| x.for_syntax)
497
        // .map(|x| x.path.get_path())
498
        {
499
            let require_for_syntax = require_object.path.get_path();
1,160✔
500

501
            let (module, in_scope_macros, mut name_mangler) = Self::find_in_scope_macros(
502
                &mut self.compiled_modules,
1,160✔
503
                require_for_syntax.as_ref(),
1,160✔
504
                &require_object,
1,160✔
505
                &mut mangled_asts,
1,160✔
506
            );
507

508
            // let kernel_macros_in_scope: HashSet<_> =
509
            //     module.provides_for_syntax.iter().cloned().collect();
510

511
            // let defmacros_exported: HashSet<_> = module.
512

513
            // dbg!(&kernel_macros_in_scope);
514

515
            let module_name = module.name.to_str().unwrap().to_string();
1,160✔
516

517
            if let Some(kernel) = kernel.as_mut() {
2,320✔
518
                if kernel.exported_defmacros(&module_name).is_some() {
159✔
519
                    lifted_kernel_environments.insert(
159✔
520
                        module_name.clone(),
159✔
521
                        KernelDefMacroSpec {
159✔
522
                            _env: module_name.clone(),
159✔
523
                            _exported: None,
159✔
524
                            name_mangler: name_mangler.clone(),
159✔
525
                        },
526
                    );
527
                }
528
            }
529

530
            // TODO: This isn't right - only check if there are defmacro things
531
            // that we need to lift - just check the values that are in the defmacros
532
            // environment in the kernel
533
            // if !kernel_macros_in_scope.is_empty() {
534
            //     lifted_kernel_environments.insert(
535
            //         module_name.clone(),
536
            //         KernelDefMacroSpec {
537
            //             env: module_name,
538
            //             exported: None,
539
            //             name_mangler: name_mangler.clone(),
540
            //         },
541
            //     );
542
            // }
543

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

546
            for expr in ast.iter_mut() {
2,876✔
547
                // @matt 12/8/2023
548
                // The easiest thing to do here, is to go to the other module, and find
549
                // what defmacros have been exposed on the require for syntax. Once those
550
                // have been found, we run a pass with kernel expansion, limiting the
551
                // expander to only use the macros that we've exposed. After that,
552
                // we run the expansion again, using the full suite of defmacro capabilities.
553
                //
554
                // The question that remains - how to define the neat phases of what kinds
555
                // of macros can expand into what? Can defmacro -> syntax-rules -> defmacro?
556
                // This could eventually prove to be cumbersome, but it is still early
557
                // for defmacro. Plus, I need to create a syntax-case or syntax-parse
558
                // frontend before the defmacro style macros become too pervasive.
559
                //
560
                // TODO: Replicate this behavior over to builtin modules
561

562
                // First expand the in scope macros
563
                // These are macros
564
                let mut expander = Expander::new(&in_scope_macros);
1,716✔
565
                expander.expand(expr)?;
1,716✔
566
                let changed = false;
1,716✔
567

568
                // (first_round_expanded, changed) = expand_kernel_in_env_with_allowed(
569
                //     first_round_expanded,
570
                //     kernel.as_mut(),
571
                //     // We don't need to expand those here
572
                //     ModuleContainer::default(),
573
                //     module.name.to_str().unwrap().to_string(),
574
                //     &kernel_macros_in_scope,
575
                // )?;
576

577
                // If the kernel expander expanded into something - go ahead
578
                // and expand all of the macros in this
579
                // if changed || expander.changed {
580
                // Expand here?
581
                // first_round_expanded = expand(first_round_expanded, &module.macro_map)?;
582

583
                // Probably don't need this
584
                // (first_round_expanded, changed) = expand_kernel_in_env_with_change(
585
                //     first_round_expanded,
586
                //     kernel.as_mut(),
587
                //     ModuleContainer::default(),
588
                //     module.name.to_str().unwrap().to_string(),
589
                // )?;
590

591
                // This is pretty suspect, and needs to be revisited - only the output of the
592
                // macro expansion and not the whole thing needs to be mangled most likely.
593
                // Otherwise, we'll run into weird stuff?
594
                // if changed {
595
                //     name_mangler.visit(&mut first_round_expanded);
596
                // }
597
                // }
598

599
                if expander.changed || changed {
3,044✔
600
                    let _source_id = sources.get_source_id(&module.name).unwrap();
388✔
601

602
                    // let mut fully_expanded = first_round_expanded;
603

604
                    expand(
605
                        expr,
388✔
606
                        &module.macro_map,
388✔
607
                        // source_id,
608
                    )?;
609

610
                    // Expanding the kernel with only these macros...
611
                    let changed = expand_kernel_in_env_with_change(
612
                        expr,
388✔
613
                        kernel.as_mut(),
388✔
614
                        // We don't need to expand those here
615
                        ModuleContainer::default(),
388✔
616
                        &module_name,
388✔
617
                        // &kernel_macros_in_scope,
618
                    )?;
619

620
                    if changed {
×
621
                        name_mangler.visit(expr);
×
622
                    }
623

624
                    // lifted_kernel_environments.insert(
625
                    //     module_name.clone(),
626
                    //     KernelDefMacroSpec {
627
                    //         env: module_name,
628
                    //         exported: None,
629
                    //         name_mangler: name_mangler.clone(),
630
                    //     },
631
                    // );
632

633
                    // Ok(fully_expanded)
634
                }
635
                // else {
636
                //     Ok(first_round_expanded)
637
                // }
638
            }
639

640
            // ast = ast
641
            //     .into_iter()
642
            //     .map(|x| {
643
            //         // @matt 12/8/2023
644
            //         // The easiest thing to do here, is to go to the other module, and find
645
            //         // what defmacros have been exposed on the require for syntax. Once those
646
            //         // have been found, we run a pass with kernel expansion, limiting the
647
            //         // expander to only use the macros that we've exposed. After that,
648
            //         // we run the expansion again, using the full suite of defmacro capabilities.
649
            //         //
650
            //         // The question that remains - how to define the neat phases of what kinds
651
            //         // of macros can expand into what? Can defmacro -> syntax-rules -> defmacro?
652
            //         // This could eventually prove to be cumbersome, but it is still early
653
            //         // for defmacro. Plus, I need to create a syntax-case or syntax-parse
654
            //         // frontend before the defmacro style macros become too pervasive.
655
            //         //
656
            //         // TODO: Replicate this behavior over to builtin modules
657

658
            //         // First expand the in scope macros
659
            //         // These are macros
660
            //         let mut expander = Expander::new(&in_scope_macros);
661
            //         let mut first_round_expanded = expander.expand(x)?;
662
            //         let mut changed = false;
663

664
            //         // (first_round_expanded, changed) = expand_kernel_in_env_with_allowed(
665
            //         //     first_round_expanded,
666
            //         //     kernel.as_mut(),
667
            //         //     // We don't need to expand those here
668
            //         //     ModuleContainer::default(),
669
            //         //     module.name.to_str().unwrap().to_string(),
670
            //         //     &kernel_macros_in_scope,
671
            //         // )?;
672

673
            //         // If the kernel expander expanded into something - go ahead
674
            //         // and expand all of the macros in this
675
            //         // if changed || expander.changed {
676
            //         // Expand here?
677
            //         // first_round_expanded = expand(first_round_expanded, &module.macro_map)?;
678

679
            //         // Probably don't need this
680
            //         // (first_round_expanded, changed) = expand_kernel_in_env_with_change(
681
            //         //     first_round_expanded,
682
            //         //     kernel.as_mut(),
683
            //         //     ModuleContainer::default(),
684
            //         //     module.name.to_str().unwrap().to_string(),
685
            //         // )?;
686

687
            //         // This is pretty suspect, and needs to be revisited - only the output of the
688
            //         // macro expansion and not the whole thing needs to be mangled most likely.
689
            //         // Otherwise, we'll run into weird stuff?
690
            //         // if changed {
691
            //         //     name_mangler.visit(&mut first_round_expanded);
692
            //         // }
693
            //         // }
694

695
            //         if expander.changed || changed {
696
            //             let source_id = sources.get_source_id(&module.name).unwrap();
697

698
            //             let mut fully_expanded = first_round_expanded;
699

700
            //             expand(
701
            //                 &mut fully_expanded,
702
            //                 &module.macro_map,
703
            //                 // source_id,
704
            //             )?;
705

706
            //             let module_name = module.name.to_str().unwrap().to_string();
707

708
            //             // Expanding the kernel with only these macros...
709
            //             let changed = expand_kernel_in_env_with_change(
710
            //                 &mut fully_expanded,
711
            //                 kernel.as_mut(),
712
            //                 // We don't need to expand those here
713
            //                 ModuleContainer::default(),
714
            //                 module_name.clone(),
715
            //                 // &kernel_macros_in_scope,
716
            //             )?;
717

718
            //             if changed {
719
            //                 name_mangler.visit(&mut fully_expanded);
720
            //             }
721

722
            //             // lifted_kernel_environments.insert(
723
            //             //     module_name.clone(),
724
            //             //     KernelDefMacroSpec {
725
            //             //         env: module_name,
726
            //             //         exported: None,
727
            //             //         name_mangler: name_mangler.clone(),
728
            //             //     },
729
            //             // );
730

731
            //             Ok(fully_expanded)
732
            //         } else {
733
            //             Ok(first_round_expanded)
734
            //         }
735
            //     })
736
            //     .collect::<Result<_>>()?;
737

738
            // Global macro map - also need to expand with ALL macros
739
            // post expansion in the target environment, which means we can't _just_
740
            // extend the global macro map with the target in scope macros, we need to
741
            // do something like the two pass expansion
742
            global_macro_map.extend(in_scope_macros);
1,160✔
743

744
            lifted_macro_environments.insert(module.name.clone());
1,160✔
745
        }
746

747
        // Include the defines from the modules now imported
748
        module_statements.append(&mut require_defines);
776✔
749

750
        // The next two lines here expand _all_ of the source code with the top level macros
751
        // This is necessary because of the std library macros, although this should be able to be
752
        // solved with scoped imports of the standard library explicitly
753
        module_statements.append(&mut ast);
776✔
754

755
        time!("Top level macro evaluation time", {
771✔
756
            for expr in module_statements.iter_mut() {
22,165✔
757
                expand(expr, global_macro_map)?;
21,394✔
758
            }
759
        });
760

761
        // @Matt 7/4/23
762
        // TODO: With mangling, this could cause problems. We'll want to un-mangle quotes AFTER the macro has been expanded,
763
        // in order to preserve the existing behavior.
764
        // let result = module_statements
765
        //     .into_iter()
766
        //     .map(|x| expand(x, global_macro_map))
767
        //     .collect::<Result<_>>();
768

769
        // result
770

771
        Ok(module_statements)
771✔
772
    }
773

774
    fn find_in_scope_macros<'a>(
2,528✔
775
        compiled_modules: &'a mut FxHashMap<PathBuf, CompiledModule>,
776
        require_for_syntax: &'a PathBuf,
777
        require_object: &'a RequireObject,
778
        mangled_asts: &'a mut Vec<ExprKind>,
779
    ) -> (
780
        &'a CompiledModule,
781
        FxHashMap<InternedString, SteelMacro>,
782
        NameMangler,
783
    ) {
784
        let module = compiled_modules
2,528✔
785
            .get_mut(require_for_syntax)
2,528✔
786
            .expect(&format!("Module missing!: {:?}", require_for_syntax));
2,528✔
787

788
        let prefix = "mangler".to_string() + module.name.to_str().unwrap() + MANGLER_SEPARATOR;
2,528✔
789

790
        let globals = collect_globals(&module.ast);
2,528✔
791

792
        let mut name_mangler = NameMangler::new(globals, prefix);
2,528✔
793

794
        // If the module hasn't been emitted already, then include it here
795
        if !module.emitted {
2,528✔
796
            let mut module_ast = module.ast.clone();
×
797

798
            name_mangler.mangle_vars(&mut module_ast);
×
799

800
            mangled_asts.append(&mut module_ast);
×
801
        }
802

803
        // let provided_macros = module.provides_for
804
        // let expander = Expander::new(&module.macro_map);
805
        // TODO
806
        // expand expressions one by one
807
        // if expansion with _just_ public macros from the required module doesn't do anything, stop
808
        // if it _does_ change, do another pass with all of the macros in scope
809
        // do this for each of the expressions in the file in this loop
810
        // TODO -> try not cloning this
811
        // TODO -> do this in the module expansion as well
812
        let mut in_scope_macros = module
2,528✔
813
            .provides_for_syntax
2,528✔
814
            .iter()
815
            // Chain with just the normal provides!
816
            // .chain(module.provides)
817
            // .filter_map(|x| module.macro_map.get(x).map(|m| (*x, m.clone()))) // TODO -> fix this unwrap
818
            // .filter_map(|x| module.macro_map.get_mut(x).map(|m| (*x, m))) // TODO -> fix this unwrap
819
            .filter_map(|x| {
5,751✔
820
                let smacro = module.macro_map.get_mut(x);
3,223✔
821

822
                if let Some(smacro) = smacro {
6,428✔
823
                    if !smacro.special_mangled {
×
824
                        for expr in smacro.exprs_mut() {
14,539✔
825
                            name_mangler.visit(expr);
5,667✔
826
                        }
827
                    }
828

829
                    Some((*x, smacro.clone()))
3,205✔
830
                } else {
831
                    None
18✔
832
                }
833

834
                // if !x.1.special_mangled {
835
                //     for expr in x.1.exprs_mut() {
836
                //         name_mangler.visit(expr);
837
                //     }
838
                // }
839

840
                // (x.0, x.1.clone())
841
            })
842
            .collect::<FxHashMap<_, _>>();
843

844
        // If the require_object specifically imports things, we should reference it
845

846
        if !require_object.idents_to_import.is_empty() {
2,528✔
847
            for maybe in &require_object.idents_to_import {
28✔
848
                match maybe {
×
849
                    MaybeRenamed::Normal(n) => {
10✔
850
                        if let Some(ident) = n.atom_identifier() {
10✔
851
                            if let Some(mut m) = module.macro_map.get(ident).cloned() {
×
852
                                for expr in m.exprs_mut() {
×
853
                                    name_mangler.visit(expr);
×
854
                                }
855

856
                                if let Some(prefix) = &require_object.prefix {
×
857
                                    in_scope_macros
×
858
                                        .insert((prefix.to_string() + ident.resolve()).into(), m);
×
859
                                } else {
860
                                    in_scope_macros.insert(*ident, m);
×
861
                                }
862
                            }
863
                        }
864
                    }
865
                    MaybeRenamed::Renamed(from, to) => {
2✔
866
                        if let Some(ident) = from.atom_identifier() {
4✔
867
                            if let Some(mut m) = module.macro_map.get(ident).cloned() {
×
868
                                for expr in m.exprs_mut() {
×
869
                                    name_mangler.visit(expr);
×
870
                                }
871
                                // TODO: Remove this unwrap
872
                                // in_scope_macros.insert(*to.atom_identifier().unwrap(), m);
873

874
                                if let Some(prefix) = &require_object.prefix {
×
875
                                    in_scope_macros.insert(
×
876
                                        (prefix.to_string()
×
877
                                            + to.atom_identifier().unwrap().resolve())
×
878
                                        .into(),
×
879
                                        m,
×
880
                                    );
881
                                } else {
882
                                    in_scope_macros.insert(*to.atom_identifier().unwrap(), m);
×
883
                                }
884
                            }
885
                        }
886
                    }
887
                }
888
            }
889
        } else {
890
            // Pull in all of the macros that the module exposes
891

892
            for provide_expr in &module.provides {
14,142✔
893
                if let Some(provide_expr) = provide_expr.list() {
5,809✔
894
                    for ident in provide_expr.args.split_first().unwrap().1 {
35,619✔
895
                        // println!("Looking for {}", ident);
896

897
                        if let Some(ident) = ident.atom_identifier() {
70,737✔
898
                            if let Some(mut m) = module.macro_map.get(ident).cloned() {
638✔
899
                                // println!("Pulling in macro: {}", ident);
900

901
                                for expr in m.exprs_mut() {
2,126✔
902
                                    name_mangler.visit(expr);
1,063✔
903
                                }
904

905
                                if let Some(prefix) = &require_object.prefix {
638✔
906
                                    in_scope_macros
×
907
                                        .insert((prefix.to_string() + ident.resolve()).into(), m);
×
908
                                } else {
909
                                    in_scope_macros.insert(*ident, m);
638✔
910
                                }
911
                            }
912
                        }
913
                    }
914
                }
915
            }
916
        }
917

918
        // Check what macros are in scope here
919
        // println!(
920
        //     "In scope macros: {:#?}",
921
        //     in_scope_macros.keys().collect::<Vec<_>>()
922
        // );
923
        (module, in_scope_macros, name_mangler)
2,528✔
924
    }
925

926
    // #[cfg(not(feature = "modules"))]
927
    // pub(crate) fn expand_expressions(
928
    //     &mut self,
929
    //     global_macro_map: &mut HashMap<InternedString, SteelMacro>,
930
    //     mut exprs: Vec<ExprKind>,
931
    // ) -> Result<Vec<ExprKind>> {
932
    //     extract_macro_defs(&mut exprs, global_macro_map)?;
933
    //     exprs
934
    //         .into_iter()
935
    //         .map(|x| expand(x, global_macro_map))
936
    //         .collect()
937
    // }
938
}
939

940
// Pre-compile module to bytecode? Is it even possible?
941
// Dynamically linking the module would then make it relatively
942
// easy to just load everything up at the start.
943
// Compiled module _should_ be possible now. Just create a target
944
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
945
pub struct CompiledModule {
946
    name: PathBuf,
947
    provides: Vec<ExprKind>,
948
    require_objects: Vec<RequireObject>,
949
    provides_for_syntax: Vec<InternedString>,
950
    pub(crate) macro_map: FxHashMap<InternedString, SteelMacro>,
951
    ast: Vec<ExprKind>,
952
    emitted: bool,
953
}
954

955
// TODO: @Matt 6/12/23 - This _should_ be serializable. If possible, we can try to store intermediate objects down to some file.
956
impl CompiledModule {
957
    pub fn new(
1,008✔
958
        name: PathBuf,
959
        provides: Vec<ExprKind>,
960
        require_objects: Vec<RequireObject>,
961
        provides_for_syntax: Vec<InternedString>,
962
        macro_map: FxHashMap<InternedString, SteelMacro>,
963
        ast: Vec<ExprKind>,
964
    ) -> Self {
965
        Self {
966
            name,
967
            provides,
968
            require_objects,
969
            provides_for_syntax,
970
            macro_map,
971
            ast,
972
            emitted: false,
973
        }
974
    }
975

976
    pub fn get_ast(&self) -> &[ExprKind] {
×
977
        &self.ast
×
978
    }
979

980
    pub fn get_provides(&self) -> &[ExprKind] {
×
981
        &self.provides
×
982
    }
983

984
    // pub fn get_requires(&self) -> &[PathBuf] {
985
    //     &self.requires
986
    // }
987

988
    pub fn set_emitted(&mut self, emitted: bool) {
1,008✔
989
        self.emitted = emitted;
1,008✔
990
    }
991

992
    fn to_top_level_module(
1,008✔
993
        &self,
994
        modules: &FxHashMap<PathBuf, CompiledModule>,
995
        global_macro_map: &FxHashMap<InternedString, SteelMacro>,
996
    ) -> Result<ExprKind> {
997
        let mut globals = collect_globals(&self.ast);
1,008✔
998

999
        let mut exprs = self.ast.clone();
1,008✔
1000

1001
        let mut provide_definitions = Vec::new();
1,008✔
1002

1003
        let prefix = "mangler".to_string() + self.name.to_str().unwrap() + MANGLER_SEPARATOR;
1,008✔
1004

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

1009
        let mut explicit_requires = HashMap::new();
1,008✔
1010

1011
        // TODO: This is the same as the top level, they should be merged
1012
        for require_object in &self.require_objects {
3,744✔
1013
            let path = require_object.path.get_path();
1014

1015
            explicit_requires.clear();
1016

1017
            for ident in &require_object.idents_to_import {
1,374✔
1018
                match ident {
1019
                    MaybeRenamed::Normal(i) => {
3✔
1020
                        explicit_requires.insert(i.atom_identifier().unwrap().clone(), None);
3✔
1021
                    }
1022
                    MaybeRenamed::Renamed(from, to) => {
×
1023
                        explicit_requires.insert(
×
1024
                            from.atom_identifier().unwrap().clone(),
×
1025
                            Some(to.atom_identifier().unwrap().clone()),
×
1026
                        );
1027
                    }
1028
                }
1029
            }
1030

1031
            // println!("{:?}", path);
1032
            // println!("{:?}", modules.keys().collect::<Vec<_>>());
1033
            let module = modules.get(path.as_ref()).unwrap();
1,368✔
1034

1035
            let other_module_prefix =
1,368✔
1036
                "mangler".to_string() + module.name.to_str().unwrap() + MANGLER_SEPARATOR;
1,368✔
1037

1038
            for provide_expr in &module.provides {
8,132✔
1039
                // For whatever reason, the value coming into module.provides is an expression like: (provide expr...)
1040
                for provide in &provide_expr.list().unwrap().args[1..] {
21,643✔
1041
                    match provide {
21,643✔
1042
                        ExprKind::List(l) => {
486✔
1043
                            if let Some(qualifier) = l.first_ident() {
972✔
1044
                                if module.macro_map.contains_key(qualifier) {
1045
                                    continue;
×
1046
                                }
1047

1048
                                match *qualifier {
486✔
1049
                                    x if x == *REQUIRE_IDENT_SPEC => {
972✔
1050
                                        // Directly expand into define/contract, but with the value just being the hash get below
1051

1052
                                        // (bind/c contract name 'name)
1053

1054
                                        let name = l.args.get(1).unwrap();
486✔
1055

1056
                                        if !explicit_requires.is_empty()
486✔
1057
                                            && !name
×
1058
                                                .atom_identifier()
×
1059
                                                .map(|x| explicit_requires.contains_key(x))
×
1060
                                                .unwrap_or_default()
1061
                                        {
1062
                                            continue;
×
1063
                                        }
1064

1065
                                        if module
486✔
1066
                                            .macro_map
486✔
1067
                                            .contains_key(name.atom_identifier().unwrap())
486✔
1068
                                        {
1069
                                            continue;
×
1070
                                        }
1071

1072
                                        let hash_get = expr_list![
486✔
1073
                                            ExprKind::atom(*PROTO_HASH_GET),
486✔
1074
                                            ExprKind::atom(
486✔
1075
                                                "__module-".to_string() + &other_module_prefix
486✔
1076
                                            ),
1077
                                            ExprKind::Quote(Box::new(Quote::new(
486✔
1078
                                                name.clone(),
486✔
1079
                                                SyntaxObject::default(TokenType::Quote)
486✔
1080
                                            ))),
1081
                                        ];
1082

1083
                                        let mut owned_name = name.clone();
486✔
1084

1085
                                        // If we have the alias listed, we should use it
1086
                                        if !explicit_requires.is_empty() {
486✔
1087
                                            if let Some(alias) = explicit_requires
×
1088
                                                .get(name.atom_identifier().unwrap())
×
1089
                                                .copied()
1090
                                                .flatten()
1091
                                            {
1092
                                                *owned_name.atom_identifier_mut().unwrap() =
1093
                                                    alias.clone();
1094
                                            }
1095
                                        }
1096

1097
                                        if let Some(prefix) = &require_object.prefix {
×
1098
                                            if let Some(existing) = owned_name.atom_identifier_mut()
×
1099
                                            {
1100
                                                let mut prefixed_identifier = prefix.clone();
×
1101
                                                prefixed_identifier.push_str(existing.resolve());
×
1102

1103
                                                // Update the existing identifier to point to a new one with the prefix applied
1104
                                                *existing = prefixed_identifier.into();
×
1105
                                            }
1106
                                        }
1107

1108
                                        globals.insert(*name.atom_identifier().unwrap());
486✔
1109

1110
                                        let define = ExprKind::Define(Box::new(Define::new(
486✔
1111
                                            owned_name,
486✔
1112
                                            hash_get,
486✔
1113
                                            SyntaxObject::default(TokenType::Define),
486✔
1114
                                        )));
1115

1116
                                        provide_definitions.push(define);
486✔
1117
                                    }
1118

1119
                                    // x if x == *CONTRACT_OUT => {
1120
                                    //     // Directly expand into define/contract, but with the value just being the hash get below
1121

1122
                                    //     // (bind/c contract name 'name)
1123

1124
                                    //     let mut name = l.args.get(1).unwrap().clone();
1125
                                    //     let _contract = l.args.get(2).unwrap();
1126

1127
                                    //     if !explicit_requires.is_empty()
1128
                                    //         && !name
1129
                                    //             .atom_identifier()
1130
                                    //             .map(|x| explicit_requires.contains_key(x))
1131
                                    //             .unwrap_or_default()
1132
                                    //     {
1133
                                    //         continue;
1134
                                    //     }
1135

1136
                                    //     // If we have the alias listed, we should use it
1137
                                    //     if !explicit_requires.is_empty() {
1138
                                    //         if let Some(alias) = explicit_requires
1139
                                    //             .get(name.atom_identifier().unwrap())
1140
                                    //             .copied()
1141
                                    //             .flatten()
1142
                                    //         {
1143
                                    //             *name.atom_identifier_mut().unwrap() =
1144
                                    //                 alias.clone();
1145
                                    //         }
1146
                                    //     }
1147

1148
                                    //     if let Some(prefix) = &require_object.prefix {
1149
                                    //         if let Some(existing) = name.atom_identifier_mut() {
1150
                                    //             let mut prefixed_identifier = prefix.clone();
1151
                                    //             prefixed_identifier.push_str(existing.resolve());
1152

1153
                                    //             // Update the existing identifier to point to a new one with the prefix applied
1154
                                    //             *existing = prefixed_identifier.into();
1155
                                    //         }
1156
                                    //     }
1157

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

1163
                                    //     let hash_get = expr_list![
1164
                                    //         ExprKind::atom(*PROTO_HASH_GET),
1165
                                    //         ExprKind::atom(
1166
                                    //             "__module-".to_string() + &other_module_prefix
1167
                                    //         ),
1168
                                    //         ExprKind::Quote(Box::new(Quote::new(
1169
                                    //             name.clone(),
1170
                                    //             SyntaxObject::default(TokenType::Quote)
1171
                                    //         ))),
1172
                                    //     ];
1173

1174
                                    //     let define = ExprKind::Define(Box::new(Define::new(
1175
                                    //         ExprKind::atom(
1176
                                    //             prefix.clone()
1177
                                    //                 + name.atom_identifier().unwrap().resolve(),
1178
                                    //         ),
1179
                                    //         hash_get,
1180
                                    //         SyntaxObject::default(TokenType::Define),
1181
                                    //     )));
1182

1183
                                    //     provide_definitions.push(define);
1184
                                    // }
1185
                                    _ => {
1186
                                        stop!(TypeMismatch => format!("provide expects either an identifier, (for-syntax <ident>), or (contract/out ...) - found: {}", provide))
×
1187
                                    }
1188
                                }
1189
                            } else {
1190
                                stop!(TypeMismatch => "provide expects either an identifier or a (for-syntax <ident>)")
×
1191
                            }
1192
                        }
1193
                        ExprKind::Atom(_) => {
1194
                            if !explicit_requires.is_empty()
21,157✔
1195
                                && !provide
5✔
1196
                                    .atom_identifier()
5✔
1197
                                    .map(|x| explicit_requires.contains_key(x))
10✔
1198
                                    .unwrap_or_default()
1199
                            {
1200
                                continue;
2✔
1201
                            }
1202

1203
                            if module
21,155✔
1204
                                .macro_map
21,155✔
1205
                                .contains_key(provide.atom_identifier().unwrap())
21,155✔
1206
                            {
1207
                                continue;
159✔
1208
                            }
1209

1210
                            // Mangle with a prefix if necessary
1211
                            let mut provide = provide.clone();
20,996✔
1212
                            let raw_provide = provide.clone();
20,996✔
1213

1214
                            // If we have the alias listed, we should use it
1215
                            if !explicit_requires.is_empty() {
20,996✔
1216
                                if let Some(alias) = explicit_requires
3✔
1217
                                    .get(provide.atom_identifier().unwrap())
3✔
1218
                                    .copied()
1219
                                    .flatten()
1220
                                {
1221
                                    *provide.atom_identifier_mut().unwrap() = alias.clone();
1222
                                }
1223
                            }
1224

1225
                            if let Some(prefix) = &require_object.prefix {
×
1226
                                if let Some(existing) = provide.atom_identifier_mut() {
×
1227
                                    let mut prefixed_identifier = prefix.clone();
×
1228
                                    prefixed_identifier.push_str(existing.resolve());
×
1229

1230
                                    // Update the existing identifier to point to a new one with the prefix applied
1231
                                    *existing = prefixed_identifier.into();
×
1232
                                }
1233
                            }
1234

1235
                            let provide_ident = provide.atom_identifier().unwrap();
20,996✔
1236

1237
                            // Since this is now bound to be in the scope of the current working module, we also want
1238
                            // this to be mangled. In the event we do something like, qualify the import, then we might
1239
                            // have to mangle this differently
1240
                            globals.insert(*provide_ident);
20,996✔
1241

1242
                            let define = ExprKind::Define(Box::new(Define::new(
20,996✔
1243
                                ExprKind::atom(prefix.clone() + provide_ident.resolve()),
20,996✔
1244
                                expr_list![
20,996✔
1245
                                    ExprKind::atom(*PROTO_HASH_GET),
20,996✔
1246
                                    ExprKind::atom("__module-".to_string() + &other_module_prefix),
20,996✔
1247
                                    ExprKind::Quote(Box::new(Quote::new(
20,996✔
1248
                                        raw_provide.clone(),
20,996✔
1249
                                        SyntaxObject::default(TokenType::Quote)
20,996✔
1250
                                    )))
1251
                                ],
1252
                                SyntaxObject::default(TokenType::Define),
20,996✔
1253
                            )));
1254

1255
                            // if require_object.prefix.is_some() {
1256
                            //     println!("{}", define);
1257
                            // }
1258

1259
                            provide_definitions.push(define);
20,996✔
1260
                        }
1261
                        _ => {
1262
                            stop!(TypeMismatch => "provide expression needs to either be a `contract/out` form or an identifier")
×
1263
                        }
1264
                    }
1265
                }
1266
            }
1267
        }
1268

1269
        // Mangle all of the variables that are either:
1270
        // 1. Defined locally in this file
1271
        // 2. Required by another file
1272
        let mut name_mangler = NameMangler::new(globals, prefix.clone());
1,008✔
1273

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

1278
        name_mangler.mangle_vars(&mut exprs);
1,008✔
1279

1280
        // The provide definitions should also be mangled
1281
        name_mangler.mangle_vars(&mut provide_definitions);
1,008✔
1282

1283
        // let mut hash_builder = Vec::new();
1284

1285
        // These are gonna be the pairs
1286
        // hash_builder.push(());
1287

1288
        // Construct the series of provides as well, we'll want these to refer to the correct values
1289
        //
1290
        let mut provides: smallvec::SmallVec<[(ExprKind, ExprKind); 24]> = self
1,008✔
1291
            .provides
1,008✔
1292
            .iter()
1293
            .flat_map(|x| &x.list().unwrap().args[1..])
3,659✔
1294
            .cloned()
1295
            .map(|x| (x.clone(), x))
11,753✔
1296
            .collect();
1297

1298
        for provide in &mut provides {
20,482✔
1299
            match &provide.1 {
1300
                ExprKind::List(l) => {
486✔
1301
                    if let Some(qualifier) = l.first_ident() {
486✔
1302
                        match qualifier {
1303
                            x if *x == *REQUIRE_IDENT_SPEC => {
486✔
1304
                                // *provide = expand(l.get(2).unwrap().clone(), global_macro_map)?;
1305

1306
                                // *provide = expand(l.)
1307

1308
                                provide.0 = l.get(1).unwrap().clone();
486✔
1309

1310
                                let mut provide_expr = l.get(2).unwrap().clone();
486✔
1311
                                expand(&mut provide_expr, global_macro_map)?;
486✔
1312

1313
                                provide.1 = provide_expr;
486✔
1314

1315
                                continue;
486✔
1316

1317
                                // name_unmangler.unmangle_expr(provide);
1318
                            }
1319
                            // x if *x == *CONTRACT_OUT => {
1320
                            //     // Update the item to point to just the name
1321
                            //     //
1322
                            //     // *provide = l.get(1).unwrap().clone();
1323
                            //     // {
1324
                            //     //     println!("---------");
1325
                            //     //     println!("Provide expr: {}", l.to_string());
1326
                            //     // }
1327

1328
                            //     provide.0 = l.get(1).unwrap().clone();
1329

1330
                            //     let mut provide_expr = expr_list![
1331
                            //         ExprKind::ident("bind/c"),
1332
                            //         l.get(2).unwrap().clone(),
1333
                            //         l.get(1).unwrap().clone(),
1334
                            //         ExprKind::Quote(Box::new(Quote::new(
1335
                            //             l.get(1).unwrap().clone(),
1336
                            //             SyntaxObject::default(TokenType::Quote)
1337
                            //         ))),
1338
                            //     ];
1339

1340
                            //     expand(&mut provide_expr, global_macro_map)?;
1341

1342
                            //     provide.1 = provide_expr;
1343

1344
                            //     name_unmangler.unmangle_expr(&mut provide.1);
1345
                            //     // continue;
1346
                            // }
1347
                            unknown => {
×
1348
                                stop!(TypeMismatch => "bar provide expects either an identifier, (for-syntax <ident>), or (contract/out ...), found: {}", unknown)
×
1349
                            }
1350
                        }
1351
                    } else {
1352
                        stop!(TypeMismatch => "provide expects either an identifier or a (for-syntax <ident>)")
×
1353
                    }
1354
                }
1355
                ExprKind::Atom(_) => {
1356
                    continue;
9,251✔
1357
                }
1358
                _ => {
1359
                    stop!(TypeMismatch => "provide expression needs to either be a `contract/out` form or an identifier")
×
1360
                }
1361
            }
1362
        }
1363

1364
        // Drop all of the macro references here
1365
        provides.retain(|x| !self.macro_map.contains_key(x.0.atom_identifier().unwrap()));
11,753✔
1366

1367
        // We want one without the mangled version, for the actual provides
1368
        let un_mangled = provides.clone();
1,008✔
1369

1370
        let left_unmangled: Vec<_> = un_mangled.into_iter().map(|x| x.0).collect();
11,274✔
1371

1372
        let mut right: Vec<_> = provides.into_iter().map(|x| x.1).collect();
11,274✔
1373

1374
        name_mangler.mangle_vars(&mut right);
1,008✔
1375
        // name_unmangler.unmangle_vars(&mut provides);
1376

1377
        let mut hash_body = vec![ExprKind::ident("hash")];
1,008✔
1378

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

1384
        // left_unmangled.pretty_print();
1385

1386
        hash_body.extend(interleave(
1,008✔
1387
            left_unmangled.into_iter().map(|x| {
10,266✔
1388
                if let ExprKind::Atom(_) = x {
9,258✔
1389
                    ExprKind::Quote(Box::new(Quote::new(
9,258✔
1390
                        x,
9,258✔
1391
                        SyntaxObject::default(TokenType::Quote),
9,258✔
1392
                    )))
1393
                } else if let ExprKind::List(l) = x {
×
1394
                    if let Some(qualifier) = l.first_ident() {
×
1395
                        match qualifier {
1396
                            x if *x == *REQUIRE_IDENT_SPEC => {
×
1397
                                todo!()
×
1398
                            }
1399
                            _ => {
1400
                                return ExprKind::Quote(Box::new(Quote::new(
×
1401
                                    l.get(2).unwrap().clone(),
×
1402
                                    SyntaxObject::default(TokenType::Quote),
×
1403
                                )))
1404
                            }
1405
                        }
1406
                    }
1407

1408
                    // Then this is a contract out, and we should handle it here
1409

1410
                    ExprKind::Quote(Box::new(Quote::new(
×
1411
                        l.get(2).unwrap().clone(),
×
1412
                        SyntaxObject::default(TokenType::Quote),
×
1413
                    )))
1414
                    // ExprKind::Quote(Box::new(Quote::new(
1415
                    //     x,
1416
                    //     SyntaxObject::default(TokenType::Quote),
1417
                    // )))
1418
                } else {
1419
                    panic!("TODO this shouldn't be possible")
×
1420
                }
1421
            }),
1422
            right,
1,008✔
1423
        ));
1424

1425
        let module_define = ExprKind::Define(Box::new(Define::new(
1,008✔
1426
            ExprKind::atom("__module-".to_string() + &prefix),
1,008✔
1427
            ExprKind::List(List::new(hash_body)),
1,008✔
1428
            SyntaxObject::default(TokenType::Quote),
1,008✔
1429
        )));
1430

1431
        // let mut offset = None;
1432

1433
        // Find offset of first non builtin require definition:
1434
        // for (idx, expr) in exprs.iter().enumerate() {
1435
        //     if let ExprKind::Define(d) = expr {
1436
        //         // if !is_a_builtin_definition(d) || !is_a_require_definition(d) {
1437
        //         if !is_a_builtin_definition(d) {
1438
        //             // println!("Found offset at: {:?}", offset);
1439

1440
        //             offset = Some(idx);
1441
        //             println!("Found offset at: {:?}", offset);
1442
        //             break;
1443
        //         }
1444
        //     }
1445
        // }
1446

1447
        exprs.push(module_define);
1,008✔
1448

1449
        // exprs.append(&mut provide_definitions);
1450

1451
        let mut builtin_definitions = Vec::new();
1,008✔
1452

1453
        exprs.retain_mut(|expr| {
197,739✔
1454
            if let ExprKind::Define(d) = expr {
388,481✔
1455
                if is_a_builtin_definition(d) {
1456
                    builtin_definitions.push(std::mem::take(expr));
168,328✔
1457
                    false
168,328✔
1458
                } else {
1459
                    true
23,422✔
1460
                }
1461
            } else {
1462
                true
4,981✔
1463
            }
1464
        });
1465

1466
        builtin_definitions.append(&mut provide_definitions);
1,008✔
1467
        builtin_definitions.append(&mut exprs);
1,008✔
1468

1469
        // provide_definitions.append(&mut builtin_definitions);
1470
        // provide_definitions.append(&mut exprs);
1471

1472
        exprs = builtin_definitions;
1,008✔
1473

1474
        // if let Some(offset) = offset {
1475
        // for (idx, expr) in provide_definitions.into_iter().enumerate() {
1476
        //     exprs.insert(offset + idx, expr);
1477
        // }
1478
        // } else {
1479
        // provide_definitions.append(&mut exprs);
1480
        // }
1481

1482
        // println!("MODULE DEFINITIONS----");
1483

1484
        // exprs.pretty_print();
1485

1486
        // println!("END MODULE DEFINITIONS");
1487

1488
        // exprs.pretty_print();
1489

1490
        // exprs.push(module_define);
1491

1492
        // Construct the overall definition
1493
        // TODO: Perhaps mangle these as well, especially if they have contracts associated with them
1494

1495
        // if offset.is_none() {
1496
        //     provide_definitions.append(&mut exprs);
1497
        // }
1498

1499
        // Try this out?
1500
        // let mut analysis = Analysis::from_exprs(&provide_definitions);
1501
        // let mut semantic = SemanticAnalysis::from_analysis(&mut provide_definitions, analysis);
1502

1503
        // // This is definitely broken still
1504
        // semantic.remove_unused_globals_with_prefix("mangler");
1505
        // .replace_non_shadowed_globals_with_builtins()
1506
        // .remove_unused_globals_with_prefix("mangler");
1507

1508
        Ok(ExprKind::Begin(Box::new(Begin::new(
1,008✔
1509
            exprs,
1,008✔
1510
            SyntaxObject::default(TokenType::Begin),
1,008✔
1511
        ))))
1512
    }
1513

1514
    // Turn the module into the AST node that represents the macro module in the stdlib
1515
    fn _to_module_ast_node(&self) -> ExprKind {
×
1516
        let mut body = vec![
×
1517
            ExprKind::Atom(Atom::new(SyntaxObject::default(TokenType::Identifier(
×
1518
                "module".into(),
×
1519
            )))),
1520
            ExprKind::Atom(Atom::new(SyntaxObject::default(TokenType::Identifier(
×
1521
                ("___".to_string() + self.name.to_str().unwrap()).into(),
×
1522
            )))),
1523
        ];
1524

1525
        // Put any provides at the top
1526
        body.append(&mut self.provides.clone());
×
1527

1528
        // Include any dependencies here
1529
        // body.append(&mut self.requires.clone());
1530

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

1537
        // self.ast.pretty_print();
1538

1539
        // body.push(steel_base);
1540

1541
        // Put the ast nodes inside the macro
1542
        body.append(&mut self.ast.clone());
×
1543

1544
        // TODO clean this up
1545
        let res = ExprKind::List(List::new(body));
×
1546

1547
        // if log_enabled!(target: "requires", log::Level::Debug) {
1548
        //     debug!(target: "requires", "Module ast node: {}", res.to_string());
1549
        // }
1550

1551
        res
×
1552
    }
1553
}
1554

1555
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
1556
enum MaybeRenamed {
1557
    Normal(ExprKind),
1558
    Renamed(ExprKind, ExprKind),
1559
}
1560

1561
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1562
pub struct RequireObject {
1563
    path: PathOrBuiltIn,
1564
    for_syntax: bool,
1565
    idents_to_import: Vec<MaybeRenamed>,
1566
    prefix: Option<String>,
1567
    span: Span,
1568
}
1569

1570
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
1571
enum PathOrBuiltIn {
1572
    BuiltIn(Cow<'static, str>),
1573
    Path(PathBuf),
1574
}
1575

1576
impl PathOrBuiltIn {
1577
    pub fn get_path(&self) -> Cow<'_, PathBuf> {
7,584✔
1578
        match self {
7,584✔
1579
            Self::Path(p) => Cow::Borrowed(p),
243✔
1580
            Self::BuiltIn(p) => Cow::Owned(PathBuf::from(p.to_string())),
7,341✔
1581
        }
1582
    }
1583
}
1584

1585
#[derive(Default, Debug, Clone)]
1586
struct RequireObjectBuilder {
1587
    path: Option<PathOrBuiltIn>,
1588
    for_syntax: bool,
1589
    idents_to_import: Vec<MaybeRenamed>,
1590
    // Built up prefix
1591
    prefix: Option<String>,
1592
    span: Span,
1593
}
1594

1595
impl RequireObjectBuilder {
1596
    fn build(self) -> Result<RequireObject> {
2,528✔
1597
        let path = self
5,056✔
1598
            .path
2,528✔
1599
            .ok_or_else(crate::throw!(Generic => "require must have a path!"))?;
2,528✔
1600

1601
        Ok(RequireObject {
1602
            path,
1603
            for_syntax: self.for_syntax,
1604
            idents_to_import: self.idents_to_import,
1605
            prefix: self.prefix,
1606
            span: self.span,
1607
        })
1608
    }
1609
}
1610

1611
fn try_canonicalize(path: PathBuf) -> PathBuf {
1,089✔
1612
    std::fs::canonicalize(&path).unwrap_or_else(|_| path)
3,133✔
1613
}
1614

1615
struct ModuleBuilder<'a> {
1616
    name: PathBuf,
1617
    main: bool,
1618
    source_ast: Vec<ExprKind>,
1619
    macro_map: FxHashMap<InternedString, SteelMacro>,
1620
    // TODO: Change the requires / requires_for_syntax to just be a require enum?
1621
    require_objects: Vec<RequireObject>,
1622

1623
    provides: Vec<ExprKind>,
1624
    provides_for_syntax: Vec<ExprKind>,
1625
    compiled_modules: &'a mut FxHashMap<PathBuf, CompiledModule>,
1626
    visited: &'a mut FxHashSet<PathBuf>,
1627
    file_metadata: &'a mut FxHashMap<PathBuf, SystemTime>,
1628
    sources: &'a mut Sources,
1629
    kernel: &'a mut Option<Kernel>,
1630
    builtin_modules: ModuleContainer,
1631
    global_macro_map: &'a FxHashMap<InternedString, SteelMacro>,
1632
    custom_builtins: &'a HashMap<String, String>,
1633
    search_dirs: &'a [PathBuf],
1634
}
1635

1636
impl<'a> ModuleBuilder<'a> {
1637
    #[allow(clippy::too_many_arguments)]
1638
    #[allow(unused)]
1639
    fn main(
776✔
1640
        name: Option<PathBuf>,
1641
        source_ast: Vec<ExprKind>,
1642
        compiled_modules: &'a mut FxHashMap<PathBuf, CompiledModule>,
1643
        visited: &'a mut FxHashSet<PathBuf>,
1644
        file_metadata: &'a mut FxHashMap<PathBuf, SystemTime>,
1645
        sources: &'a mut Sources,
1646
        kernel: &'a mut Option<Kernel>,
1647
        builtin_modules: ModuleContainer,
1648
        global_macro_map: &'a FxHashMap<InternedString, SteelMacro>,
1649
        custom_builtins: &'a HashMap<String, String>,
1650
        search_dirs: &'a [PathBuf],
1651
    ) -> Result<Self> {
1652
        // TODO don't immediately canonicalize the path unless we _know_ its coming from a path
1653
        // change the path to not always be required
1654
        // if its not required we know its not coming in
1655

1656
        #[cfg(not(target_arch = "wasm32"))]
1657
        let name = if let Some(p) = name {
1,561✔
1658
            std::fs::canonicalize(p)?
×
1659
        } else {
1660
            std::env::current_dir()?
767✔
1661
        };
1662

1663
        #[cfg(target_arch = "wasm32")]
1664
        let name = PathBuf::new();
×
1665

1666
        Ok(ModuleBuilder {
×
1667
            name,
×
1668
            main: true,
×
1669
            source_ast,
×
1670
            macro_map: default_prelude_macros(),
×
1671
            require_objects: Vec::new(),
×
1672
            provides: Vec::new(),
×
1673
            provides_for_syntax: Vec::new(),
×
1674
            compiled_modules,
×
1675
            visited,
×
1676
            file_metadata,
×
1677
            sources,
×
1678
            kernel,
×
1679
            builtin_modules,
×
1680
            global_macro_map,
×
1681
            custom_builtins,
×
1682
            search_dirs,
×
1683
        })
1684
    }
1685

1686
    fn compile(&mut self) -> Result<Vec<ExprKind>> {
1,784✔
1687
        // debug!(target: "requires", "Visiting: {:?}", self.name);
1688

1689
        // @Matt - 10/3/23
1690
        // This has a relatively fatal flaw at the moment:
1691
        /*
1,784✔
1692

1693
            (define-syntax register-plugin
1,784✔
1694
              (syntax-rules ()
1,784✔
1695
                [(register-plugin plugin-path identifiers ...)
1,784✔
1696
                 (begin
1,784✔
1697
                   (require plugin-path
1,784✔
1698
                            (only-in identifiers ...))
1,784✔
1699
                   (provide identifiers ...))]))
1,784✔
1700
        */
1,784✔
1701
        // This will fail to compile - anything that expands into a require will fail since
1702
        // require is more or less a top level primitive. What we need to do is figure
1703
        // out a way to have this be recursive - the whole compilation step should
1704
        // go again once we discover that there is another require. It shouldn't be too bad,
1705
        // but the base case has to be figured out such that we don't get infinite recursion.
1706
        // I think the condition is probably something along the following:
1707
        //
1708
        // - Did we expand anything
1709
        // - Are there require statements
1710
        //
1711
        // If we expanded anything, we then should check for require statements... maybe
1712

1713
        let mut new_exprs = Vec::new();
1,784✔
1714

1715
        // self.source_ast.pretty_print();
1716

1717
        self.collect_requires()?;
1,784✔
1718
        self.collect_provides()?;
1,784✔
1719

1720
        if log_enabled!(log::Level::Info) {
1,784✔
1721
            // debug!(target: "requires", "Requires: {:#?}", self.require_objects);
1722
            // debug!(target: "requires", "Provides: {:#?}", self.provides);
1723
            // debug!(target: "requires", "Provides for-syntax: {:?}", self.provides_for_syntax);
1724
        }
1725

1726
        if self.visited.contains(&self.name) {
1,784✔
1727
            stop!(Generic => format!("circular dependency found during module resolution with: {:?}", self.name))
×
1728
        }
1729

1730
        self.visited.insert(self.name.clone());
1,784✔
1731

1732
        if self.main {
2,560✔
1733
            let exprs = std::mem::take(&mut self.source_ast);
776✔
1734
            self.source_ast = exprs
776✔
1735
                .into_iter()
776✔
1736
                .filter(|x| {
7,646✔
1737
                    if let ExprKind::List(l) = x {
13,723✔
1738
                        if let Some(provide) = l.first_ident() {
2,347✔
1739
                            return *provide != *PROVIDE;
2,347✔
1740
                        }
1741
                    }
1742
                    true
4,523✔
1743
                })
1744
                .collect();
776✔
1745
        }
1746

1747
        self.extract_macro_defs()?;
×
1748

1749
        // TODO include built ins here
1750
        if self.require_objects.is_empty() && !self.main {
2,839✔
1751
            // We're at a leaf, put into the cache
1752
            new_exprs.push(self.compile_module()?);
478✔
1753
        } else {
1754
            // TODO come back for parsing built ins
1755
            for module in self
3,753✔
1756
                .require_objects
1,306✔
1757
                .iter()
1,306✔
1758
                .filter(|x| matches!(x.path, PathOrBuiltIn::BuiltIn(_)))
6,362✔
1759
                .map(|x| x.path.get_path())
2,447✔
1760
            {
1761
                // We've established nothing has changed with this file
1762
                // Check to see if its in the cache first
1763
                // Otherwise go ahead and compile
1764
                // If we already have compiled this module, get it from the cache
1765
                if let Some(_m) = self.compiled_modules.get(module.as_ref()) {
3,939✔
1766
                    // debug!("Getting {:?} from the module cache", module);
1767
                    // println!("Already found in the cache: {:?}", module);
1768
                    // new_exprs.push(m.to_module_ast_node());
1769
                    // No need to do anything
1770
                    continue;
×
1771
                }
1772

1773
                // TODO this is some bad crap here don't do this
1774
                let input = BUILT_INS
1,910✔
1775
                    .iter()
1776
                    .find(|x| x.0 == module.to_str().unwrap())
9,545✔
1777
                    .map(|x| Cow::Borrowed(x.1))
2,865✔
1778
                    .or_else(|| {
955✔
1779
                        self.custom_builtins
×
1780
                            .get(module.to_str().unwrap())
×
1781
                            .map(|x| Cow::Owned(x.to_string()))
×
1782
                    })
1783
                    .ok_or_else(
1784
                        crate::throw!(Generic => "Unable to find builtin module: {:?}", module),
955✔
1785
                    )?;
1786

1787
                let mut new_module = ModuleBuilder::new_built_in(
1788
                    module.into_owned(),
×
1789
                    input,
×
1790
                    self.compiled_modules,
×
1791
                    self.visited,
×
1792
                    self.file_metadata,
×
1793
                    self.sources,
×
1794
                    self.kernel,
×
1795
                    self.builtin_modules.clone(),
×
1796
                    self.global_macro_map,
×
1797
                    self.custom_builtins,
×
1798
                )?;
1799

1800
                // Walk the tree and compile any dependencies
1801
                // This will eventually put the module in the cache
1802
                let mut module_exprs = new_module.compile()?;
955✔
1803

1804
                new_exprs.append(&mut module_exprs);
×
1805

1806
                // Probably want to evaluate a module even if it has no provides?
1807
                if !new_module.provides.is_empty() {
×
1808
                    new_exprs.push(new_module.compile_module()?);
477✔
1809
                } else {
1810
                    // log::debug!(target: "requires", "Found no provides, skipping compilation of module: {:?}", new_module.name);
1811
                }
1812
            }
1813

1814
            // At this point, requires should be fully qualified (absolute) paths
1815

1816
            for (module, require_statement_span) in self
1,387✔
1817
                .require_objects
1,306✔
1818
                .iter()
1,306✔
1819
                .filter(|x| matches!(x.path, PathOrBuiltIn::Path(_)))
7,668✔
1820
                .map(|x| (x.path.get_path(), x.span))
2,693✔
1821
            {
1822
                if cfg!(target_arch = "wasm32") {
1823
                    stop!(Generic => "requiring modules is not supported for wasm");
×
1824
                }
1825

1826
                let last_modified = std::fs::metadata(module.as_ref())
81✔
1827
                    .map_err(|err| {
×
1828
                        let mut err = crate::SteelErr::from(err);
×
1829
                        err.prepend_message(&format!(
×
1830
                            "Attempting to load module from: {:?} ",
×
1831
                            module
×
1832
                        ));
1833
                        err.set_span(require_statement_span)
×
1834
                    })?
1835
                    .modified()?;
1836

1837
                // Check if we should compile based on the last time modified
1838
                // If we're unable to get information, we want to compile
1839
                let should_recompile =
×
1840
                    if let Some(cached_modified) = self.file_metadata.get(module.as_ref()) {
28✔
1841
                        &last_modified != cached_modified
×
1842
                    } else {
1843
                        true
53✔
1844
                    };
1845

1846
                // We've established nothing has changed with this file
1847
                // Check to see if its in the cache first
1848
                // Otherwise go ahead and compile
1849
                if !should_recompile {
×
1850
                    // If we already have compiled this module, get it from the cache
1851
                    if let Some(_m) = self.compiled_modules.get(module.as_ref()) {
56✔
1852
                        // debug!("Getting {:?} from the module cache", module);
1853
                        // println!("Already found in the cache: {:?}", module);
1854
                        // new_exprs.push(m.to_module_ast_node());
1855
                        // No need to do anything
1856
                        continue;
×
1857
                    }
1858
                }
1859

1860
                let mut new_module = ModuleBuilder::new_from_path(
1861
                    module.into_owned(),
53✔
1862
                    self.compiled_modules,
53✔
1863
                    self.visited,
53✔
1864
                    self.file_metadata,
53✔
1865
                    self.sources,
53✔
1866
                    self.kernel,
53✔
1867
                    self.builtin_modules.clone(),
53✔
1868
                    self.global_macro_map,
53✔
1869
                    self.custom_builtins,
53✔
1870
                    self.search_dirs,
53✔
1871
                )?;
1872

1873
                // Walk the tree and compile any dependencies
1874
                // This will eventually put the module in the cache
1875
                let mut module_exprs = new_module.compile()?;
53✔
1876

1877
                // debug!("Inside {:?} - append {:?}", self.name, module);
1878
                // if log_enabled!(log::Level::Debug) {
1879
                //     debug!(
1880
                //         target: "modules",
1881
                //         "appending with {:?}",
1882
                //         module_exprs.iter().map(|x| x.to_string()).join(" SEP ")
1883
                //     );
1884
                // }
1885

1886
                new_exprs.append(&mut module_exprs);
×
1887

1888
                // TODO evaluate this
1889

1890
                // let mut ast = std::mem::replace(&mut new_module.source_ast, Vec::new());
1891
                // ast.append(&mut module_exprs);
1892
                // new_module.source_ast = ast;
1893

1894
                // dbg!(&new_module.name);
1895
                // dbg!(&new_module.compiled_modules.contains_key(&new_module.name));
1896

1897
                // If we need to, revisit because there are new provides
1898
                if !new_module.provides.is_empty() {
×
1899
                    new_exprs.push(new_module.compile_module()?);
52✔
1900
                // If the module hasn't yet been compiled, compile it anyway
1901
                } else if !new_module.compiled_modules.contains_key(&new_module.name) {
1✔
1902
                    // else if !new_module.compiled_modules.contains_key(&new_module.name) {
1903
                    new_exprs.push(new_module.compile_module()?);
1✔
1904
                } else {
1905
                    // log::debug!(target: "requires", "Found no provides, skipping compilation of module: {:?}", new_module.name);
1906
                    // log::debug!(target: "requires", "Module already in the cache: {}", new_module.compiled_modules.contains_key(&new_module.name));
1907
                    // log::debug!(target: "requires", "Compiled modules: {:?}", new_module.compiled_modules.keys().collect::<Vec<_>>());
1908
                }
1909

1910
                // else {
1911
                //     log::debug!(target: "requires", "Found no provides, skipping compilation of module: {:?}", new_module.name);
1912
                // }
1913
            }
1914
        }
1915

1916
        // new_exprs.pretty_print();
1917

1918
        Ok(new_exprs)
1,784✔
1919
    }
1920

1921
    // TODO: This should run again on itself, probably
1922
    fn compile_module(&mut self) -> Result<ExprKind> {
1,008✔
1923
        let mut ast = std::mem::take(&mut self.source_ast);
1,008✔
1924
        let mut provides = std::mem::take(&mut self.provides);
1,008✔
1925
        // Clone the requires... I suppose
1926
        let requires = self.require_objects.clone();
1,008✔
1927

1928
        // info!(
1929
        //     target: "requires",
1930
        //     "Into compiled module: provides for syntax: {:?}",
1931
        //     self.provides_for_syntax
1932
        // );
1933

1934
        // Attempt extracting the syntax transformers from this module
1935
        if let Some(kernel) = self.kernel.as_mut() {
2,016✔
1936
            kernel.load_syntax_transformers(&mut ast, self.name.to_str().unwrap().to_string())?
×
1937
        };
1938

1939
        for expr in ast.iter_mut() {
15,419✔
1940
            expand(expr, &self.macro_map)?;
14,411✔
1941

1942
            expand_kernel_in_env(
1943
                expr,
14,411✔
1944
                self.kernel.as_mut(),
14,411✔
1945
                self.builtin_modules.clone(),
14,411✔
1946
                // Expanding macros in the environment?
1947
                self.name.to_str().unwrap(),
14,411✔
1948
            )?;
1949

1950
            expand(expr, &self.macro_map)?;
14,411✔
1951
        }
1952

1953
        // Expand first with the macros from *this* module
1954
        // ast = ast
1955
        //     .into_iter()
1956
        //     .map(|x| {
1957
        //         expand(x, &self.macro_map)
1958
        //             .and_then(|x| {
1959
        //                 expand_kernel_in_env(
1960
        //                     x,
1961
        //                     self.kernel.as_mut(),
1962
        //                     self.builtin_modules.clone(),
1963
        //                     // Expanding macros in the environment?
1964
        //                     self.name.to_str().unwrap().to_string(),
1965
        //                 )
1966
        //             })
1967
        //             // Check here - I think it makes sense to expand the
1968
        //             // internal macros again, given now we might have defmacro
1969
        //             // style macros that get expanded into syntax-rules ones?
1970
        //             .and_then(|x| expand(x, &self.macro_map))
1971
        //     })
1972
        //     .collect::<Result<Vec<_>>>()?;
1973

1974
        for expr in provides.iter_mut() {
2,651✔
1975
            expand(expr, &self.macro_map)?;
1,643✔
1976
            // .and_then(|x| {
1977
            // expand_kernel(x, self.kernel.as_mut(), self.builtin_modules.clone())
1978
            expand_kernel_in_env(
1979
                expr,
1,643✔
1980
                self.kernel.as_mut(),
1,643✔
1981
                self.builtin_modules.clone(),
1,643✔
1982
                // Expanding macros in the environment?
1983
                &self.name.to_str().unwrap(),
1,643✔
1984
            )?;
1985
            // })
1986
        }
1987

1988
        // Expand provides for any macros that exist within there
1989
        // provides = provides
1990
        //     .into_iter()
1991
        //     .map(|x| {
1992
        //         expand(x, &self.macro_map).and_then(|x| {
1993
        //             // expand_kernel(x, self.kernel.as_mut(), self.builtin_modules.clone())
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
        //     })
2003
        //     .collect::<Result<Vec<_>>>()?;
2004

2005
        let mut mangled_asts = Vec::with_capacity(ast.len() + 16);
1,008✔
2006

2007
        // Look for the modules in the requires for syntax
2008
        for require_object in self.require_objects.iter()
2,376✔
2009
        // .filter(|x| x.for_syntax)
2010
        {
2011
            let require_for_syntax = require_object.path.get_path();
1,368✔
2012

2013
            let (module, in_scope_macros, mut name_mangler) = ModuleManager::find_in_scope_macros(
2014
                self.compiled_modules,
1,368✔
2015
                require_for_syntax.as_ref(),
1,368✔
2016
                &require_object,
1,368✔
2017
                &mut mangled_asts,
1,368✔
2018
            );
2019

2020
            // let kernel_macros_in_scope: HashSet<_> =
2021
            //     module.provides_for_syntax.iter().cloned().collect();
2022

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

2025
            for expr in ast.iter_mut() {
14,832✔
2026
                // First expand the in scope macros
2027
                // These are macros
2028
                let mut expander = Expander::new(&in_scope_macros);
13,464✔
2029
                expander.expand(expr)?;
13,464✔
2030
                let changed = false;
13,464✔
2031

2032
                // dbg!(expander.changed);
2033

2034
                // (first_round_expanded, changed) = expand_kernel_in_env_with_allowed(
2035
                //     first_round_expanded,
2036
                //     self.kernel.as_mut(),
2037
                //     // We don't need to expand those here
2038
                //     ModuleContainer::default(),
2039
                //     module.name.to_str().unwrap().to_string(),
2040
                //     &kernel_macros_in_scope,
2041
                // )?;
2042

2043
                // If the kernel expander expanded into something - go ahead
2044
                // and expand all of the macros in this
2045
                // if changed || expander.changed {
2046
                // Expand here?
2047
                // first_round_expanded = expand(first_round_expanded, &module.macro_map)?;
2048

2049
                // Probably don't need this
2050
                // (first_round_expanded, changed) = expand_kernel_in_env_with_change(
2051
                //     first_round_expanded,
2052
                //     self.kernel.as_mut(),
2053
                //     ModuleContainer::default(),
2054
                //     module.name.to_str().unwrap().to_string(),
2055
                // )?;
2056

2057
                // name_mangler.visit(&mut first_round_expanded);
2058
                // }
2059

2060
                if expander.changed || changed {
26,134✔
2061
                    // let source_id = self.sources.get_source_id(&module.name).unwrap();
2062

2063
                    let mut fully_expanded = expr;
794✔
2064
                    expand(&mut fully_expanded, &module.macro_map)?;
794✔
2065

2066
                    // Expanding the kernel with only these macros...
2067
                    let changed = expand_kernel_in_env_with_change(
2068
                        &mut fully_expanded,
794✔
2069
                        self.kernel.as_mut(),
794✔
2070
                        // We don't need to expand those here
2071
                        ModuleContainer::default(),
794✔
2072
                        &module.name.to_str().unwrap(),
794✔
2073
                        // &kernel_macros_in_scope,
2074
                    )?;
2075

2076
                    if changed {
17✔
2077
                        name_mangler.visit(&mut fully_expanded);
17✔
2078
                    }
2079

2080
                    // lifted_kernel_environments.insert(
2081
                    //     module_name.clone(),
2082
                    //     KernelDefMacroSpec {
2083
                    //         env: module_name,
2084
                    //         exported: None,
2085
                    //         name_mangler: name_mangler.clone(),
2086
                    //     },
2087
                    // );
2088

2089
                    // Ok(fully_expanded)
2090
                }
2091
                // else {
2092
                //     Ok(first_round_expanded)
2093
                // }
2094
            }
2095

2096
            // ast = ast
2097
            //     .into_iter()
2098
            //     .map(|x| {
2099
            //         // First expand the in scope macros
2100
            //         // These are macros
2101
            //         let mut expander = Expander::new(&in_scope_macros);
2102
            //         let mut first_round_expanded = expander.expand(x)?;
2103
            //         let mut changed = false;
2104

2105
            //         // dbg!(expander.changed);
2106

2107
            //         // (first_round_expanded, changed) = expand_kernel_in_env_with_allowed(
2108
            //         //     first_round_expanded,
2109
            //         //     self.kernel.as_mut(),
2110
            //         //     // We don't need to expand those here
2111
            //         //     ModuleContainer::default(),
2112
            //         //     module.name.to_str().unwrap().to_string(),
2113
            //         //     &kernel_macros_in_scope,
2114
            //         // )?;
2115

2116
            //         // If the kernel expander expanded into something - go ahead
2117
            //         // and expand all of the macros in this
2118
            //         // if changed || expander.changed {
2119
            //         // Expand here?
2120
            //         // first_round_expanded = expand(first_round_expanded, &module.macro_map)?;
2121

2122
            //         // Probably don't need this
2123
            //         // (first_round_expanded, changed) = expand_kernel_in_env_with_change(
2124
            //         //     first_round_expanded,
2125
            //         //     self.kernel.as_mut(),
2126
            //         //     ModuleContainer::default(),
2127
            //         //     module.name.to_str().unwrap().to_string(),
2128
            //         // )?;
2129

2130
            //         // name_mangler.visit(&mut first_round_expanded);
2131
            //         // }
2132

2133
            //         if expander.changed || changed {
2134
            //             // let source_id = self.sources.get_source_id(&module.name).unwrap();
2135

2136
            //             let mut fully_expanded = first_round_expanded;
2137
            //             expand(&mut fully_expanded, &module.macro_map)?;
2138

2139
            //             let module_name = module.name.to_str().unwrap().to_string();
2140

2141
            //             // Expanding the kernel with only these macros...
2142
            //             let changed = expand_kernel_in_env_with_change(
2143
            //                 &mut fully_expanded,
2144
            //                 self.kernel.as_mut(),
2145
            //                 // We don't need to expand those here
2146
            //                 ModuleContainer::default(),
2147
            //                 module_name.clone(),
2148
            //                 // &kernel_macros_in_scope,
2149
            //             )?;
2150

2151
            //             if changed {
2152
            //                 name_mangler.visit(&mut fully_expanded);
2153
            //             }
2154

2155
            //             // lifted_kernel_environments.insert(
2156
            //             //     module_name.clone(),
2157
            //             //     KernelDefMacroSpec {
2158
            //             //         env: module_name,
2159
            //             //         exported: None,
2160
            //             //         name_mangler: name_mangler.clone(),
2161
            //             //     },
2162
            //             // );
2163

2164
            //             Ok(fully_expanded)
2165
            //         } else {
2166
            //             Ok(first_round_expanded)
2167
            //         }
2168
            //     })
2169
            //     .collect::<Result<_>>()?;
2170

2171
            for expr in provides.iter_mut() {
2,728✔
2172
                // First expand the in scope macros
2173
                // These are macros
2174
                let mut expander = Expander::new(&in_scope_macros);
1,360✔
2175
                expander.expand(expr)?;
1,360✔
2176

2177
                if expander.changed {
1,360✔
2178
                    expand(expr, &module.macro_map)?
160✔
2179
                }
2180
                // else {
2181
                //     Ok(first_round_expanded)
2182
                // }
2183
            }
2184

2185
            // provides = provides
2186
            //     .into_iter()
2187
            //     .map(|x| {
2188
            //         // First expand the in scope macros
2189
            //         // These are macros
2190
            //         let mut expander = Expander::new(&in_scope_macros);
2191
            //         let first_round_expanded = expander.expand(x)?;
2192

2193
            //         expander.expand(&mut expr);
2194

2195
            //         if expander.changed {
2196
            //             expand(first_round_expanded, &module.macro_map)
2197
            //         } else {
2198
            //             Ok(first_round_expanded)
2199
            //         }
2200
            //     })
2201
            //     .collect::<Result<_>>()?;
2202
        }
2203

2204
        // let requires_before = self.require_objects.len();
2205

2206
        // self.collect_requires()?;
2207

2208
        // if self.require_objects.len() > requires_before {
2209
        //     println!("EXPANDED INTO A REQUIRE");
2210
        // }
2211

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

2215
        // TODO: @Matt - fix this hack
2216
        {
2217
            self.source_ast = ast;
1,008✔
2218
            self.provides = provides;
1,008✔
2219

2220
            self.collect_provides()?;
1,008✔
2221

2222
            // let requires_before = self.require_objects.len();
2223

2224
            // self.collect_requires()?;
2225

2226
            // if self.require_objects.len() > requires_before {
2227
            //     println!("EXPANDED INTO A REQUIRE");
2228
            // }
2229

2230
            provides = std::mem::take(&mut self.provides);
1,008✔
2231
            ast = std::mem::take(&mut self.source_ast);
1,008✔
2232
        }
2233

2234
        // Put the mangled asts at the top
2235
        // then include the ast there
2236
        mangled_asts.append(&mut ast);
1,008✔
2237

2238
        for expr in mangled_asts.iter_mut() {
195,723✔
2239
            lower_entire_ast(expr)?;
195,723✔
2240

2241
            FlattenBegin::flatten(expr);
195,723✔
2242
        }
2243

2244
        // mangled_asts = mangled_asts
2245
        //     .into_iter()
2246
        //     .map(lower_entire_ast)
2247
        //     // We want this to at least be flattened for querying later
2248
        //     .map(|x| {
2249
        //         x.map(|mut o| {
2250
        //             FlattenBegin::flatten(&mut o);
2251
        //             o
2252
        //         })
2253
        //     })
2254
        //     .collect::<std::result::Result<_, ParseError>>()?;
2255

2256
        // Take ast, expand with self modules, then expand with each of the require for-syntaxes
2257
        // Then mangle the require-for-syntax, include the mangled directly in the ast
2258

2259
        // TODO: Come back here - we're going to need to figure out the require objects
2260
        let mut module = CompiledModule::new(
2261
            self.name.clone(),
1,008✔
2262
            provides,
1,008✔
2263
            requires,
1,008✔
2264
            self.provides_for_syntax
1,008✔
2265
                .iter()
1,008✔
2266
                .map(|x| *x.atom_identifier().unwrap())
2,681✔
2267
                .collect(),
1,008✔
2268
            std::mem::take(&mut self.macro_map),
1,008✔
2269
            mangled_asts,
1,008✔
2270
        );
2271

2272
        module.set_emitted(true);
1,008✔
2273

2274
        // println!(
2275
        //     "-------------- Emitting module: {:?} ----------------------",
2276
        //     self.name
2277
        // );
2278

2279
        let result = module.to_top_level_module(self.compiled_modules, self.global_macro_map)?;
2,016✔
2280

2281
        // println!("{}", result.to_pretty(60));
2282

2283
        // println!("------------------ Finish ----------------------------------");
2284

2285
        // let mut analysis = Analysis::from_exprs(&[result]);
2286

2287
        // let mut semantic = SemanticAnalysis::from_analysis(&mut result, analysis);
2288

2289
        // // This is definitely broken still
2290
        // semantic
2291
        //     .remove_unused_globals_with_prefix("mangler");
2292

2293
        // log::debug!(target: "requires", "Adding compiled module: {:?}", self.name);
2294
        // println!("Adding compiled module: {:?}", self.name);
2295
        // for (key, smacro) in module.macro_map.iter() {
2296
        //     println!("{}", key.resolve());
2297
        //     for expr in smacro.exprs() {
2298
        //         println!("{}", expr);
2299
        //     }
2300
        // }
2301

2302
        self.compiled_modules.insert(self.name.clone(), module);
1,008✔
2303

2304
        Ok(result)
1,008✔
2305
    }
2306

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

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

2313
        let mut error = None;
1,784✔
2314

2315
        self.source_ast.retain_mut(|expr| {
25,160✔
2316
            if let ExprKind::Macro(_) = expr {
23,376✔
2317
                // Replace with dummy begin value so we don't have to copy
2318
                // everything other for every macro definition
2319
                let mut taken_expr = ExprKind::Begin(Box::new(Begin::new(
1,459✔
2320
                    Vec::new(),
1,459✔
2321
                    SyntaxObject::default(TokenType::Begin),
1,459✔
2322
                )));
2323

2324
                std::mem::swap(expr, &mut taken_expr);
1,459✔
2325

2326
                if let ExprKind::Macro(m) = taken_expr {
1,459✔
2327
                    match SteelMacro::parse_from_ast_macro(m) {
×
2328
                        Ok(generated_macro) => {
1,459✔
2329
                            let name = generated_macro.name();
1,459✔
2330
                            self.macro_map.insert(*name, generated_macro);
1,459✔
2331
                        }
2332
                        Err(e) => {
×
2333
                            if error.is_none() {
×
2334
                                error = Some(e);
×
2335
                            }
2336
                            // error = Some(e);
2337
                            return false;
×
2338
                        }
2339
                    }
2340
                } else {
2341
                    unreachable!();
×
2342
                }
2343

2344
                return false;
1,459✔
2345
            }
2346

2347
            true
21,917✔
2348
        });
2349

2350
        if let Some(e) = error {
1,784✔
2351
            return Err(e);
×
2352
        }
2353

2354
        Ok(())
1,784✔
2355
    }
2356

2357
    // Takes out the (for-syntax) forms from the provides
2358
    fn filter_out_for_syntax_provides(&mut self, exprs: Vec<ExprKind>) -> Result<Vec<ExprKind>> {
1,667✔
2359
        let mut normal_provides = Vec::new();
1,667✔
2360

2361
        for expr in exprs {
28,567✔
2362
            match &expr {
×
2363
                ExprKind::Atom(_) => {
12,299✔
2364
                    normal_provides.push(expr);
12,299✔
2365
                }
2366
                ExprKind::List(l) => {
1,151✔
2367
                    if let Some(for_syntax) = l.first_ident() {
1,151✔
2368
                        match *for_syntax {
486✔
2369
                            x if x == *FOR_SYNTAX => {
1,330✔
2370
                                if l.args.len() != 2 {
665✔
2371
                                    stop!(ArityMismatch => "provide expects a single identifier in the (for-syntax <ident>)"; l.location)
×
2372
                                }
2373

2374
                                // Collect the for syntax expressions
2375
                                // TODO -> remove this clone
2376
                                self.provides_for_syntax.push(l.args[1].clone());
665✔
2377
                            }
2378
                            x if x == *REQUIRE_IDENT_SPEC => {
486✔
2379
                                normal_provides.push(expr);
×
2380
                            }
2381
                            // x if x == *CONTRACT_OUT || x == *REQUIRE_IDENT_SPEC => {
2382
                            //     normal_provides.push(expr);
2383
                            // }
2384
                            _ => {
486✔
2385
                                normal_provides.push(expr);
486✔
2386
                                // stop!(TypeMismatch => "provide expects either an identifier, (for-syntax <ident>), or (contract/out ...)")
2387
                            }
2388
                        }
2389
                    } else {
2390
                        stop!(TypeMismatch => "provide expects either an identifier or a (for-syntax <ident>)"; l.location)
×
2391
                    }
2392
                }
2393
                _ => {
×
2394
                    stop!(TypeMismatch => "provide expects either a (for-syntax <ident>) or an ident"; opt expr.span())
×
2395
                }
2396
            }
2397
        }
2398

2399
        Ok(normal_provides)
1,667✔
2400
    }
2401

2402
    // TODO -> collect (provide (for-syntax ...))
2403
    // I think these will already be collected for the macro, however I think for syntax should be found earlier
2404
    // Otherwise the macro expansion will not be able to understand it
2405
    fn collect_provides(&mut self) -> Result<()> {
3,568✔
2406
        // let now = std::time::Instant::now();
2407

2408
        let mut non_provides = Vec::with_capacity(self.source_ast.len());
3,568✔
2409
        let exprs = std::mem::take(&mut self.source_ast);
3,568✔
2410

2411
        fn walk(
5,714✔
2412
            module_builder: &mut ModuleBuilder,
2413
            exprs: Vec<ExprKind>,
2414
            non_provides: &mut Vec<ExprKind>,
2415
        ) -> Result<()> {
2416
            for mut expr in exprs {
460,986✔
2417
                match &mut expr {
×
2418
                    ExprKind::List(l) => {
43,382✔
2419
                        if let Some(provide) = l.first_ident() {
10,835✔
2420
                            if *provide == *PROVIDE {
×
2421
                                if l.len() == 1 {
1,667✔
2422
                                    stop!(Generic => "provide expects at least one identifier to provide"; l.location);
×
2423
                                }
2424

2425
                                // Swap out the value inside the list
2426
                                let args = std::mem::take(&mut l.args);
1,667✔
2427

2428
                                let filtered =
1,667✔
2429
                                    module_builder.filter_out_for_syntax_provides(args)?;
×
2430

2431
                                l.args = filtered;
1,667✔
2432

2433
                                module_builder.provides.push(expr);
1,667✔
2434

2435
                                continue;
1,667✔
2436
                            }
2437
                        }
2438
                    }
2439
                    ExprKind::Begin(b) => {
2,146✔
2440
                        let exprs = std::mem::take(&mut b.exprs);
2,146✔
2441

2442
                        // Reserve capacity for these to be moved to the top level
2443
                        non_provides.reserve(exprs.len());
2,146✔
2444

2445
                        walk(module_builder, exprs, non_provides)?;
2,146✔
2446
                    }
2447
                    _ => {}
182,108✔
2448
                }
2449

2450
                non_provides.push(expr);
225,969✔
2451
            }
2452

2453
            Ok(())
5,714✔
2454
        }
2455

2456
        walk(self, exprs, &mut non_provides)?;
3,568✔
2457

2458
        self.source_ast = non_provides;
3,568✔
2459

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

2462
        Ok(())
3,568✔
2463
    }
2464

2465
    fn parse_require_object(
2,528✔
2466
        &mut self,
2467
        home: &Option<PathBuf>,
2468
        r: &crate::parser::ast::Require,
2469
        atom: &ExprKind,
2470
    ) -> Result<RequireObject> {
2471
        let mut object = RequireObjectBuilder::default();
2,528✔
2472

2473
        // Set the span so we can pass through an error if the
2474
        // module is not found
2475
        object.span = r.location.span;
2,528✔
2476

2477
        self.parse_require_object_inner(home, r, atom, &mut object)
2,528✔
2478
            .and_then(|_| object.build())
7,584✔
2479
    }
2480

2481
    // TODO: Recursively crunch the requires to gather up the necessary information
2482
    fn parse_require_object_inner(
2,533✔
2483
        &mut self,
2484
        home: &Option<PathBuf>,
2485
        r: &crate::parser::ast::Require,
2486
        atom: &ExprKind,
2487
        require_object: &mut RequireObjectBuilder,
2488
    ) -> Result<()> {
2489
        match atom {
1,776✔
2490
            ExprKind::Atom(Atom {
×
2491
                syn:
×
2492
                    SyntaxObject {
×
2493
                        ty: TokenType::StringLiteral(s),
1,776✔
2494
                        span,
1,776✔
2495
                        ..
1,776✔
2496
                    },
2497
            }) => {
1,776✔
2498
                if require_object.path.is_some() {
1,776✔
2499
                    stop!(Generic => "require object only expects one path!")
×
2500
                }
2501

2502
                // Try this?
2503
                if let Some(lib) = BUILT_INS.into_iter().cloned().find(|x| x.0 == s.as_str()) {
16,621✔
2504
                    // self.built_ins.push(PathBuf::from(lib.0));
2505

2506
                    require_object.path = Some(PathOrBuiltIn::BuiltIn(lib.0.into()));
1,705✔
2507

2508
                    return Ok(());
1,705✔
2509
                    // continue;
2510
                }
2511

2512
                if self.custom_builtins.contains_key(s.as_str()) {
71✔
2513
                    require_object.path =
×
2514
                        Some(PathOrBuiltIn::BuiltIn(s.clone().to_string().into()));
×
2515

2516
                    return Ok(());
×
2517
                }
2518

2519
                if cfg!(target_arch = "wasm32") {
2520
                    stop!(Generic => "requiring modules is not supported for wasm");
×
2521
                }
2522

2523
                let mut current = self.name.clone();
71✔
2524
                if current.is_file() {
113✔
2525
                    current.pop();
42✔
2526
                }
2527
                current.push(PathBuf::from(s.as_str()));
×
2528

2529
                // // If the path exists on its own, we can continue
2530
                // // But theres the case where we're searching for a module on the STEEL_HOME
2531
                if !current.exists() {
×
2532
                    if let Some(mut home) = home.clone() {
42✔
2533
                        home.push(PathBuf::from(s.as_str()));
×
2534
                        current = home;
×
2535

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

2538
                        if !current.exists() {
21✔
2539
                            for dir in self.search_dirs {
×
2540
                                let mut dir = dir.clone();
×
2541
                                dir.push(s.as_str());
×
2542

2543
                                if dir.exists() {
×
2544
                                    current = dir;
×
2545
                                    break;
×
2546
                                }
2547
                            }
2548
                        }
2549
                    } else {
2550
                        // TODO: Check if this module exists in STEEL_HOME first. If it does, we'll take that as our candidate
2551
                        // and then continue on to the final module resolution part.
2552
                        //
2553
                        // If it doesn't exist, we should iterate through the search directories and attempt to find
2554
                        // a matching path there.
2555

2556
                        for dir in self.search_dirs {
×
2557
                            let mut dir = dir.clone();
×
2558
                            dir.push(s.as_str());
×
2559

2560
                            if dir.exists() {
×
2561
                                current = dir;
×
2562
                                break;
×
2563
                            }
2564
                        }
2565

2566
                        stop!(Generic => format!("Module not found: {:?} with STEEL_HOME: {:?}", current, home); *span)
×
2567
                    }
2568
                }
2569

2570
                // Get the absolute path and store that
2571
                // self.requires.push(current)
2572

2573
                let current = try_canonicalize(current);
71✔
2574

2575
                require_object.path = Some(PathOrBuiltIn::Path(current));
71✔
2576
            }
2577

2578
            // TODO: Requires with qualifiers, that aren't just for-syntax
2579
            // Perhaps like:
2580
            // (with-prefix <xyz>)
2581
            ExprKind::List(l) => {
757✔
2582
                match l.first_ident() {
757✔
2583
                    Some(x) if *x == *ONLY_IN => {
765✔
2584
                        if l.args.len() < 2 {
4✔
2585
                            stop!(BadSyntax => "only-in expects a require-spec and optionally a list of ids to bind (maybe renamed)"; l.location);
×
2586
                        }
2587

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

2590
                        for remaining in &l.args[2..] {
16✔
2591
                            match remaining {
12✔
2592
                                ExprKind::Atom(_) => {
10✔
2593
                                    require_object
10✔
2594
                                        .idents_to_import
10✔
2595
                                        .push(MaybeRenamed::Normal(remaining.clone()));
10✔
2596
                                }
2597
                                ExprKind::List(l) => {
2✔
2598
                                    if l.len() != 2 {
2✔
2599
                                        stop!(BadSyntax => "Expected a pair when renaming required identifiers");
×
2600
                                    }
2601

2602
                                    let from = &l.args[0];
2✔
2603
                                    let to = &l.args[1];
2✔
2604

2605
                                    if from.atom_identifier().is_none()
2✔
2606
                                        || to.atom_identifier().is_none()
2✔
2607
                                    {
2608
                                        stop!(BadSyntax => "only-in expected identifiers to rename");
×
2609
                                    }
2610

2611
                                    // (<from> <to>)
2612
                                    require_object
2✔
2613
                                        .idents_to_import
2✔
2614
                                        .push(MaybeRenamed::Renamed(from.clone(), to.clone()));
2✔
2615
                                }
2616
                                _ => {
×
2617
                                    stop!(BadSyntax => "unexpected syntax in only-in form during module requires")
×
2618
                                }
2619
                            }
2620
                        }
2621
                    }
2622

2623
                    Some(x) if *x == *PREFIX_IN => {
755✔
2624
                        if l.args.len() != 3 {
1✔
2625
                            stop!(BadSyntax => "prefix-in expects a prefix to prefix a given file or module"; l.location);
×
2626
                        }
2627

2628
                        let prefix = &l.args[1];
1✔
2629

2630
                        if let Some(prefix) = prefix.atom_identifier() {
1✔
2631
                            match &mut require_object.prefix {
×
2632
                                Some(existing_prefix) => {
×
2633
                                    // Append the new symbol to the existing prefix
2634
                                    existing_prefix.push_str(prefix.resolve());
×
2635
                                }
2636
                                None => {
1✔
2637
                                    require_object.prefix = Some(prefix.resolve().to_string());
1✔
2638
                                }
2639
                            }
2640

2641
                            self.parse_require_object_inner(home, r, &l.args[2], require_object)?;
×
2642
                        } else {
2643
                            stop!(TypeMismatch => "prefix-in expects an identifier to use for the prefix"; opt prefix.span());
×
2644
                        }
2645
                    }
2646

2647
                    Some(x) if *x == *FOR_SYNTAX => {
1,504✔
2648
                        // We're expecting something like (for-syntax "foo")
2649
                        if l.args.len() != 2 {
×
2650
                            stop!(BadSyntax => "for-syntax expects one string literal referring to a file or module"; l.location);
×
2651
                        }
2652

2653
                        let mod_name = &l.args[1];
752✔
2654
                        if let Some(path) = mod_name.string_literal() {
752✔
2655
                            if let Some(lib) = BUILT_INS.iter().find(|x| x.0 == path) {
6,798✔
2656
                                // self.built_ins.push(PathBuf::from(lib.0));
2657

2658
                                require_object.path = Some(PathOrBuiltIn::BuiltIn(lib.0.into()));
742✔
2659
                                require_object.for_syntax = true;
742✔
2660

2661
                                return Ok(());
742✔
2662
                                // continue;
2663
                            } else if self.custom_builtins.contains_key(path) {
10✔
2664
                                require_object.path =
×
2665
                                    Some(PathOrBuiltIn::BuiltIn(Cow::Owned(path.to_string())));
×
2666
                                require_object.for_syntax = true;
×
2667

2668
                                return Ok(());
×
2669
                            } else {
2670
                                let mut current = self.name.clone();
10✔
2671
                                if current.is_file() {
20✔
2672
                                    current.pop();
10✔
2673
                                }
2674
                                current.push(PathBuf::from(path));
×
2675

2676
                                if !current.exists() {
×
2677
                                    if let Some(mut home) = home.clone() {
10✔
2678
                                        home.push(PathBuf::from(path));
×
2679
                                        current = home;
×
2680

2681
                                        if !current.exists() {
×
2682
                                            for dir in self.search_dirs {
×
2683
                                                let mut dir = dir.clone();
×
2684
                                                dir.push(path);
×
2685

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

2693
                                        log::info!("Searching STEEL_HOME for {:?}", current);
5✔
2694
                                    } else {
2695
                                        for dir in self.search_dirs {
×
2696
                                            let mut dir = dir.clone();
×
2697
                                            dir.push(path);
×
2698

2699
                                            if dir.exists() {
×
2700
                                                current = dir;
×
2701
                                                break;
×
2702
                                            }
2703
                                        }
2704

2705
                                        stop!(Generic => format!("Module not found: {:?}", current); mod_name.span().unwrap())
×
2706
                                    }
2707
                                }
2708

2709
                                require_object.for_syntax = true;
10✔
2710
                                let current = try_canonicalize(current);
10✔
2711
                                require_object.path = Some(PathOrBuiltIn::Path(current));
10✔
2712
                            }
2713
                        } else {
2714
                            stop!(BadSyntax => "for-syntax expects a string literal referring to a file or module"; opt mod_name.span());
×
2715
                        }
2716
                    }
2717
                    _ => {
×
2718
                        stop!(BadSyntax => "require accepts either a string literal, a for-syntax expression or an only-in expression"; l.location)
×
2719
                    }
2720
                }
2721
            }
2722

2723
            unknown => {
×
2724
                stop!(Generic => format!("require object expected a string literal referring to a file/module, found: {}", unknown); opt atom.span())
×
2725
            }
2726
        }
2727

2728
        Ok(())
86✔
2729
    }
2730

2731
    fn collect_requires(&mut self) -> Result<()> {
1,784✔
2732
        // unimplemented!()
2733

2734
        let mut exprs_without_requires = Vec::new();
1,784✔
2735
        let exprs = std::mem::take(&mut self.source_ast);
1,784✔
2736

2737
        let home = STEEL_HOME
1,784✔
2738
            .clone()
2739
            .map(|x| {
3,568✔
2740
                // TODO: Fix this - try to hack in a root drive
2741
                // for windows if a unix path is provided
2742
                if cfg!(target_os = "windows") {
1,784✔
2743
                    let mut result = x.trim_start_matches("/").to_string();
×
2744

2745
                    let mut iter = result.chars();
×
2746
                    iter.next();
×
2747
                    if matches!(iter.next(), Some(':')) {
×
2748
                        return PathBuf::from(result);
×
2749
                    }
2750

2751
                    result.insert(1, ':');
×
2752
                    return PathBuf::from(result);
×
2753
                }
2754

2755
                PathBuf::from(x)
1,784✔
2756
            })
2757
            .map(|mut x| {
3,568✔
2758
                x.push("cogs");
1,784✔
2759

2760
                x
1,784✔
2761
            });
2762

2763
        fn walk(
1,784✔
2764
            module_builder: &mut ModuleBuilder,
2765
            home: &Option<PathBuf>,
2766
            exprs_without_requires: &mut Vec<ExprKind>,
2767
            exprs: Vec<ExprKind>,
2768
        ) -> Result<()> {
2769
            for expr in exprs {
56,264✔
2770
                match expr {
×
2771
                    // Include require/for-syntax here
2772
                    // This way we have some understanding of what dependencies a file has
2773
                    ExprKind::Require(r) => {
2,197✔
2774
                        for atom in &r.modules {
7,253✔
2775
                            // TODO: Consider making this not a reference for r
2776
                            let require_object =
2,528✔
2777
                                module_builder.parse_require_object(&home, &r, atom)?;
×
2778

2779
                            module_builder.require_objects.push(require_object);
2,528✔
2780
                        }
2781
                    }
2782
                    ExprKind::Begin(b) => {
×
2783
                        walk(module_builder, home, exprs_without_requires, b.exprs)?
×
2784
                    }
2785
                    _ => exprs_without_requires.push(expr),
25,043✔
2786
                }
2787
            }
2788

2789
            Ok(())
1,784✔
2790
        }
2791

2792
        walk(self, &home, &mut exprs_without_requires, exprs)?;
1,784✔
2793

2794
        self.source_ast = exprs_without_requires;
1,784✔
2795
        Ok(())
1,784✔
2796
    }
2797

2798
    #[allow(clippy::too_many_arguments)]
2799
    fn new_built_in(
955✔
2800
        name: PathBuf,
2801
        input: Cow<'static, str>,
2802
        compiled_modules: &'a mut FxHashMap<PathBuf, CompiledModule>,
2803
        visited: &'a mut FxHashSet<PathBuf>,
2804
        file_metadata: &'a mut FxHashMap<PathBuf, SystemTime>,
2805
        sources: &'a mut Sources,
2806
        kernel: &'a mut Option<Kernel>,
2807
        builtin_modules: ModuleContainer,
2808
        global_macro_map: &'a FxHashMap<InternedString, SteelMacro>,
2809
        custom_builtins: &'a HashMap<String, String>,
2810
    ) -> Result<Self> {
2811
        ModuleBuilder::raw(
2812
            name,
955✔
2813
            compiled_modules,
955✔
2814
            visited,
955✔
2815
            file_metadata,
955✔
2816
            sources,
955✔
2817
            kernel,
955✔
2818
            builtin_modules,
955✔
2819
            global_macro_map,
955✔
2820
            custom_builtins,
955✔
2821
            &[],
955✔
2822
        )
2823
        .parse_builtin(input)
955✔
2824
    }
2825

2826
    fn new_from_path(
53✔
2827
        name: PathBuf,
2828
        compiled_modules: &'a mut FxHashMap<PathBuf, CompiledModule>,
2829
        visited: &'a mut FxHashSet<PathBuf>,
2830
        file_metadata: &'a mut FxHashMap<PathBuf, SystemTime>,
2831
        sources: &'a mut Sources,
2832
        kernel: &'a mut Option<Kernel>,
2833
        builtin_modules: ModuleContainer,
2834
        global_macro_map: &'a FxHashMap<InternedString, SteelMacro>,
2835
        custom_builtins: &'a HashMap<String, String>,
2836
        search_dirs: &'a [PathBuf],
2837
    ) -> Result<Self> {
2838
        ModuleBuilder::raw(
2839
            name,
53✔
2840
            compiled_modules,
53✔
2841
            visited,
53✔
2842
            file_metadata,
53✔
2843
            sources,
53✔
2844
            kernel,
53✔
2845
            builtin_modules,
53✔
2846
            global_macro_map,
53✔
2847
            custom_builtins,
53✔
2848
            search_dirs,
53✔
2849
        )
2850
        .parse_from_path()
2851
    }
2852

2853
    fn raw(
1,008✔
2854
        name: PathBuf,
2855
        compiled_modules: &'a mut FxHashMap<PathBuf, CompiledModule>,
2856
        visited: &'a mut FxHashSet<PathBuf>,
2857
        file_metadata: &'a mut FxHashMap<PathBuf, SystemTime>,
2858
        sources: &'a mut Sources,
2859
        kernel: &'a mut Option<Kernel>,
2860
        builtin_modules: ModuleContainer,
2861
        global_macro_map: &'a FxHashMap<InternedString, SteelMacro>,
2862
        custom_builtins: &'a HashMap<String, String>,
2863
        search_dirs: &'a [PathBuf],
2864
    ) -> Self {
2865
        // println!("New module found: {:?}", name);
2866

2867
        let name = try_canonicalize(name);
1,008✔
2868

2869
        ModuleBuilder {
2870
            name,
2871
            main: false,
2872
            source_ast: Vec::new(),
1,008✔
2873
            // TODO: This used to be empty
2874
            macro_map: default_prelude_macros(),
1,008✔
2875
            // macro_map: global_macro_map.clone(),
2876
            require_objects: Vec::new(),
1,008✔
2877
            provides: Vec::new(),
1,008✔
2878
            provides_for_syntax: Vec::new(),
1,008✔
2879
            compiled_modules,
2880
            visited,
2881
            file_metadata,
2882
            sources,
2883
            kernel,
2884
            builtin_modules,
2885
            global_macro_map,
2886
            custom_builtins,
2887
            search_dirs,
2888
        }
2889
    }
2890

2891
    fn parse_builtin(mut self, input: Cow<'static, str>) -> Result<Self> {
955✔
2892
        #[cfg(feature = "profiling")]
2893
        let now = std::time::Instant::now();
955✔
2894

2895
        let id = self
955✔
2896
            .sources
955✔
2897
            .add_source(input.clone(), Some(self.name.clone()));
955✔
2898

2899
        let parsed = Parser::new_from_source(&input, self.name.clone(), Some(id))
1,910✔
2900
            .without_lowering()
2901
            .map(|x| x.and_then(lower_macro_and_require_definitions))
19,892✔
2902
            .collect::<std::result::Result<Vec<_>, ParseError>>()?;
2903

2904
        self.source_ast = parsed;
×
2905

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

2909
        // self.source_ast.pretty_print();
2910

2911
        Ok(self)
×
2912
    }
2913

2914
    fn parse_from_path(mut self) -> Result<Self> {
53✔
2915
        log::info!("Opening: {:?}", self.name);
53✔
2916

2917
        let mut file = std::fs::File::open(&self.name).map_err(|err| {
106✔
2918
            let mut err = crate::SteelErr::from(err);
×
2919
            err.prepend_message(&format!("Attempting to load module from: {:?}", self.name));
×
2920
            err
×
2921
        })?;
2922
        self.file_metadata
×
2923
            .insert(self.name.clone(), file.metadata()?.modified()?);
53✔
2924

2925
        // TODO: DEFAULT MODULE LOADER PREFIX
2926
        let mut exprs = String::new();
53✔
2927

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

2931
        let mut expressions = Parser::new(&PRELUDE_STRING, SourceId::none())
53✔
2932
            .without_lowering()
2933
            .map(|x| x.and_then(lower_macro_and_require_definitions))
424✔
2934
            .collect::<std::result::Result<Vec<_>, ParseError>>()?;
2935

2936
        // let expressions = Parser::new_from_source(, , )
2937

2938
        // Add the modules here:
2939

2940
        // exprs.push_str(ALL_MODULES);
2941

2942
        file.read_to_string(&mut exprs)?;
×
2943

2944
        let id = self.sources.add_source(exprs, Some(self.name.clone()));
53✔
2945

2946
        {
2947
            // Fetch the exprs after adding them to the sources
2948
            // We did _just_ add it, so its fine to unwrap
2949
            let guard = self.sources.sources.lock().unwrap();
53✔
2950

2951
            let exprs = guard.get(id).unwrap();
53✔
2952

2953
            let mut parsed = Parser::new_from_source(&exprs, self.name.clone(), Some(id))
53✔
2954
                .without_lowering()
2955
                .map(|x| x.and_then(lower_macro_and_require_definitions))
784✔
2956
                .collect::<std::result::Result<Vec<_>, ParseError>>()?;
2957

2958
            expressions.append(&mut parsed);
53✔
2959

2960
            self.source_ast = expressions;
53✔
2961
        }
2962

2963
        Ok(self)
53✔
2964
    }
2965
}
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