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

mattwparas / steel / 11926495124

20 Nov 2024 04:01AM UTC coverage: 46.234% (-0.7%) from 46.956%
11926495124

push

github

web-flow
Fix provides within macros (#290)

* provide fixes

* add some tests

* update the test

* add more tests

* more general fixes

* clean up warnings

* set default timeout for jobs

* reduce size of tests

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

158 existing lines in 14 files now uncovered.

12338 of 26686 relevant lines covered (46.23%)

377719.48 hits per line

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

75.87
/crates/steel-core/src/compiler/compiler.rs
1
use crate::{
2
    compiler::{
3
        constants::ConstantMap,
4
        map::SymbolMap,
5
        modules::MANGLER_PREFIX,
6
        passes::{
7
            analysis::SemanticAnalysis, begin::flatten_begins_and_expand_defines,
8
            shadow::RenameShadowedVariables, VisitorMutRefUnit,
9
        },
10
    },
11
    core::{instructions::u24, labels::Expr},
12
    parser::{
13
        expand_visitor::{expand_kernel_in_env, expand_kernel_in_env_with_change},
14
        interner::InternedString,
15
        kernel::Kernel,
16
        parser::{lower_entire_ast, lower_macro_and_require_definitions},
17
    },
18
    steel_vm::{cache::MemoizationTable, engine::ModuleContainer, primitives::constant_primitives},
19
};
20
use crate::{
21
    core::{instructions::Instruction, opcode::OpCode},
22
    parser::parser::Sources,
23
};
24

25
use std::{borrow::Cow, iter::Iterator};
26
use std::{
27
    collections::{HashMap, HashSet},
28
    path::PathBuf,
29
};
30

31
// TODO: Replace the usages of hashmap with this directly
32
use fxhash::{FxBuildHasher, FxHashMap, FxHashSet};
33
use serde::{Deserialize, Serialize};
34
use steel_parser::ast::PROVIDE;
35

36
use crate::rvals::{Result, SteelVal};
37

38
use crate::parser::ast::ExprKind;
39
use crate::parser::expander::SteelMacro;
40
use crate::parser::parser::SyntaxObject;
41
use crate::parser::parser::{ParseError, Parser};
42
use crate::parser::tokens::TokenType;
43

44
// use crate::core::instructions::{densify, DenseInstruction};
45

46
use crate::stop;
47

48
use crate::steel_vm::const_evaluation::ConstantEvaluatorManager;
49

50
use super::{
51
    constants::SerializableConstantMap,
52
    modules::{CompiledModule, ModuleManager},
53
    passes::{analysis::Analysis, mangle::NameMangler},
54
    program::RawProgramWithSymbols,
55
};
56

57
use crate::values::HashMap as ImmutableHashMap;
58

59
#[cfg(feature = "profiling")]
60
use std::time::Instant;
61

62
#[derive(Default)]
63
pub struct DebruijnIndicesInterner {
64
    flat_defines: HashSet<InternedString>,
65
    second_pass_defines: HashSet<InternedString>,
66
}
67

68
impl DebruijnIndicesInterner {
69
    pub fn collect_first_pass_defines(
24,231✔
70
        &mut self,
71
        instructions: &mut [Instruction],
72
        symbol_map: &mut SymbolMap,
73
    ) -> Result<()> {
74
        for i in 2..instructions.len() {
1,739,247✔
75
            match (&instructions[i], &instructions[i - 1], &instructions[i - 2]) {
1,715,016✔
76
                (
77
                    Instruction {
78
                        op_code: OpCode::BIND,
79
                        contents:
80
                            Some(Expr::Atom(SyntaxObject {
81
                                ty: TokenType::Identifier(s),
17,302✔
82
                                // span,
83
                                ..
17,302✔
84
                            })),
85
                        ..
17,302✔
86
                    },
87
                    Instruction {
17,302✔
88
                        op_code: OpCode::EDEF,
17,302✔
89
                        ..
17,302✔
90
                    },
91
                    Instruction {
17,302✔
92
                        op_code: OpCode::ECLOSURE,
17,302✔
93
                        ..
17,302✔
94
                    },
95
                ) => {
17,302✔
96
                    let idx = symbol_map.add(s);
17,302✔
97
                    self.flat_defines.insert(s.to_owned());
17,302✔
98
                    // if !self.flat_defines.insert(s.to_owned()) {
99
                    //     stop!(BadSyntax => format!("Cannot redefine define within the same scope: {}", s); *span);
100
                    // }
101

102
                    if let Some(x) = instructions.get_mut(i) {
34,604✔
103
                        x.payload_size = u24::from_usize(idx);
104
                    }
105
                }
106
                (
107
                    Instruction {
108
                        op_code: OpCode::BIND,
109
                        contents:
110
                            Some(Expr::Atom(SyntaxObject {
111
                                ty: TokenType::Identifier(s),
36,014✔
112
                                // span,
113
                                ..
36,014✔
114
                            })),
115
                        ..
36,014✔
116
                    },
117
                    ..,
36,014✔
118
                ) => {
36,014✔
119
                    let idx = symbol_map.add(s);
36,014✔
120
                    self.flat_defines.insert(s.to_owned());
36,014✔
121
                    // if !self.flat_defines.insert(s.to_owned()) {
122
                    //     stop!(BadSyntax => format!("Cannot redefine define within the same scope: {}", s); *span);
123
                    // }
124

125
                    if let Some(x) = instructions.get_mut(i) {
72,028✔
126
                        x.payload_size = u24::from_usize(idx);
127
                    }
128
                }
129
                _ => {}
1,661,700✔
130
            }
131
        }
132

133
        Ok(())
24,231✔
134
    }
135

136
    pub fn collect_second_pass_defines(
24,227✔
137
        &mut self,
138
        instructions: &mut [Instruction],
139
        symbol_map: &mut SymbolMap,
140
    ) -> Result<()> {
141
        // let mut second_pass_defines: HashSet<String> = HashSet::new();
142

143
        let mut depth = 0;
24,227✔
144

145
        // name mangle
146
        // Replace all identifiers with indices
147
        for i in 0..instructions.len() {
1,787,610✔
148
            match &instructions[i] {
1,763,383✔
149
                Instruction {
150
                    op_code: OpCode::SCLOSURE | OpCode::NEWSCLOSURE | OpCode::PUREFUNC,
151
                    ..
152
                } => {
37,692✔
153
                    depth += 1;
37,692✔
154
                }
155
                Instruction {
156
                    op_code: OpCode::ECLOSURE,
157
                    ..
158
                } => {
37,692✔
159
                    depth -= 1;
37,692✔
160
                }
161
                Instruction {
162
                    op_code: OpCode::BEGINSCOPE,
163
                    ..
164
                } => {
24,607✔
165
                    depth += 1;
24,607✔
166
                }
167
                Instruction {
168
                    op_code: OpCode::LETENDSCOPE,
169
                    ..
170
                } => {
24,605✔
171
                    depth -= 1;
24,605✔
172
                }
173
                Instruction {
174
                    op_code: OpCode::BIND,
175
                    contents:
176
                        Some(Expr::Atom(SyntaxObject {
177
                            ty: TokenType::Identifier(s),
53,312✔
178
                            ..
53,312✔
179
                        })),
180
                    ..
53,312✔
181
                } => {
53,312✔
182
                    // Keep track of where the defines actually are in the process
183
                    self.second_pass_defines.insert(s.to_owned());
53,312✔
184
                }
185
                Instruction {
186
                    op_code: OpCode::PUSH,
187
                    contents:
188
                        Some(Expr::Atom(SyntaxObject {
189
                            ty: TokenType::Identifier(s),
53,266✔
190
                            span,
53,266✔
191
                            ..
192
                        })),
193
                    ..
194
                }
195
                | Instruction {
196
                    op_code: OpCode::SET,
197
                    contents:
198
                        Some(Expr::Atom(SyntaxObject {
199
                            ty: TokenType::Identifier(s),
9,010✔
200
                            span,
9,010✔
201
                            ..
202
                        })),
203
                    ..
204
                } => {
205
                    if self.flat_defines.get(s).is_some()
62,276✔
206
                        && self.second_pass_defines.get(s).is_none()
47,676✔
207
                        && depth == 0
203✔
208
                    {
209
                        let message =
1✔
210
                            format!("Cannot reference an identifier before its definition: {s}");
1✔
211
                        stop!(FreeIdentifier => message; *span);
1✔
212
                    }
213

214
                    let idx = symbol_map.get(s).map_err(|e| e.set_span(*span))?;
62,289✔
215

216
                    // TODO commenting this for now
217
                    if let Some(x) = instructions.get_mut(i) {
62,261✔
218
                        x.payload_size = u24::from_usize(idx);
219
                    }
220
                }
221
                Instruction {
222
                    op_code: OpCode::CALLGLOBAL,
223
                    contents:
224
                        Some(Expr::Atom(SyntaxObject {
225
                            ty: TokenType::Identifier(s),
238,238✔
226
                            span,
238,238✔
227
                            ..
228
                        })),
229
                    ..
230
                }
231
                | Instruction {
232
                    op_code: OpCode::CALLGLOBALTAIL,
233
                    contents:
234
                        Some(Expr::Atom(SyntaxObject {
235
                            ty: TokenType::Identifier(s),
38,074✔
236
                            span,
38,074✔
237
                            ..
238
                        })),
239
                    ..
240
                } => {
241
                    if self.flat_defines.get(s).is_some()
276,312✔
242
                        && self.second_pass_defines.get(s).is_none()
61,607✔
243
                        && depth == 0
11,071✔
244
                    {
245
                        let message =
1✔
246
                            format!("Cannot reference an identifier before its definition: {s}");
1✔
247
                        stop!(FreeIdentifier => message; *span);
1✔
248
                    }
249

250
                    let idx = symbol_map.get(s).map_err(|e| e.set_span(*span))?;
276,317✔
251

252
                    // TODO commenting this for now
253
                    if let Some(x) = instructions.get_mut(i) {
276,305✔
254
                        x.payload_size = u24::from_usize(idx);
255
                    }
256
                }
257
                _ => {}
1,246,887✔
258
            }
259
        }
260

261
        Ok(())
24,205✔
262
    }
263
}
264

265
#[derive(Clone, Copy, PartialEq, PartialOrd, Serialize, Deserialize)]
266
pub enum OptLevel {
267
    Zero = 0,
268
    One,
269
    Two,
270
    Three,
271
}
272

273
#[derive(Clone)]
274
pub(crate) struct KernelDefMacroSpec {
275
    pub(crate) _env: String,
276
    pub(crate) _exported: Option<HashSet<InternedString>>,
277
    pub(crate) name_mangler: NameMangler,
278
}
279

280
#[derive(Clone)]
281
pub struct Compiler {
282
    pub(crate) symbol_map: SymbolMap,
283
    pub(crate) constant_map: ConstantMap,
284
    pub(crate) macro_env: FxHashMap<InternedString, SteelMacro>,
285
    module_manager: ModuleManager,
286
    opt_level: OptLevel,
287
    pub(crate) kernel: Option<Kernel>,
288
    memoization_table: MemoizationTable,
289
    mangled_identifiers: FxHashSet<InternedString>,
290
    // Try this out?
291
    lifted_kernel_environments: HashMap<String, KernelDefMacroSpec>,
292
    // Macros that... we need to compile against directly at the top level
293
    // This is really just a hack, but it solves cases for interactively
294
    // running at the top level using private macros.
295
    lifted_macro_environments: HashSet<PathBuf>,
296

297
    analysis: Analysis,
298
    shadowed_variable_renamer: RenameShadowedVariables,
299

300
    search_dirs: Vec<PathBuf>,
301

302
    // Include all the sources, module container, and constants
303
    // so that we can reference those at runtime. We probably should
304
    // just ignore the constants function in general. This unfortunately,
305
    // is under the hood, shared references to the engine, since we
306
    // want to have the compiler share everything with the runtime.
307
    sources: Sources,
308
    builtin_modules: ModuleContainer,
309
}
310

311
#[derive(Serialize, Deserialize)]
312
pub struct SerializableCompiler {
313
    pub(crate) symbol_map: SymbolMap,
314
    pub(crate) constant_map: SerializableConstantMap,
315
    pub(crate) macro_env: FxHashMap<InternedString, SteelMacro>,
316
    pub(crate) opt_level: OptLevel,
317
    pub(crate) module_manager: ModuleManager,
318
}
319

320
impl SerializableCompiler {
321
    #[allow(unused)]
322
    pub(crate) fn into_compiler(self) -> Compiler {
×
323
        let mut compiler = Compiler::default();
×
324

325
        compiler.symbol_map = self.symbol_map;
×
326
        compiler.constant_map = ConstantMap::from_serialized(self.constant_map).unwrap();
×
327
        compiler.macro_env = self.macro_env;
×
328
        compiler.opt_level = self.opt_level;
×
329
        compiler.module_manager = self.module_manager;
×
330

331
        compiler
×
332
    }
333
}
334

335
impl Compiler {
336
    #[allow(unused)]
337
    pub(crate) fn into_serializable_compiler(self) -> Result<SerializableCompiler> {
×
338
        Ok(SerializableCompiler {
×
339
            symbol_map: self.symbol_map,
×
340
            constant_map: self.constant_map.into_serializable_map(),
×
341
            macro_env: self.macro_env,
×
342
            opt_level: self.opt_level,
×
343
            module_manager: self.module_manager,
×
344
        })
345
    }
346
}
347

348
impl Default for Compiler {
349
    fn default() -> Self {
×
350
        Compiler::new(
351
            SymbolMap::new(),
×
352
            ConstantMap::new(),
×
353
            FxHashMap::default(),
×
354
            ModuleManager::default(),
×
355
            Sources::default(),
×
356
            ModuleContainer::default(),
×
357
        )
358
    }
359
}
360

361
impl Compiler {
362
    fn new(
5✔
363
        symbol_map: SymbolMap,
364
        constant_map: ConstantMap,
365
        macro_env: FxHashMap<InternedString, SteelMacro>,
366
        module_manager: ModuleManager,
367
        sources: Sources,
368
        builtin_modules: ModuleContainer,
369
    ) -> Compiler {
370
        Compiler {
371
            symbol_map,
372
            constant_map,
373
            macro_env,
374
            module_manager,
375
            opt_level: OptLevel::Three,
376
            kernel: None,
377
            memoization_table: MemoizationTable::new(),
5✔
378
            mangled_identifiers: FxHashSet::default(),
5✔
379
            lifted_kernel_environments: HashMap::new(),
5✔
380
            lifted_macro_environments: HashSet::new(),
5✔
381
            analysis: Analysis::pre_allocated(),
5✔
382
            shadowed_variable_renamer: RenameShadowedVariables::default(),
5✔
383
            search_dirs: Vec::new(),
5✔
384
            sources,
385
            builtin_modules,
386
        }
387
    }
388

389
    fn new_with_kernel(
×
390
        symbol_map: SymbolMap,
391
        constant_map: ConstantMap,
392
        macro_env: FxHashMap<InternedString, SteelMacro>,
393
        module_manager: ModuleManager,
394
        kernel: Kernel,
395
        sources: Sources,
396
        builtin_modules: ModuleContainer,
397
    ) -> Compiler {
398
        Compiler {
399
            symbol_map,
400
            constant_map,
401
            macro_env,
402
            module_manager,
403
            opt_level: OptLevel::Three,
404
            kernel: Some(kernel),
×
405
            memoization_table: MemoizationTable::new(),
×
406
            mangled_identifiers: FxHashSet::default(),
×
407
            lifted_kernel_environments: HashMap::new(),
×
408
            lifted_macro_environments: HashSet::new(),
×
409
            analysis: Analysis::pre_allocated(),
×
410
            shadowed_variable_renamer: RenameShadowedVariables::default(),
×
411
            search_dirs: Vec::new(),
×
412
            sources,
413
            builtin_modules,
414
        }
415
    }
416

417
    pub fn default_with_kernel(sources: Sources, builtin_modules: ModuleContainer) -> Compiler {
×
418
        Compiler::new_with_kernel(
419
            SymbolMap::new(),
×
420
            ConstantMap::new(),
×
421
            FxHashMap::default(),
×
422
            ModuleManager::default(),
×
423
            Kernel::new(),
×
424
            sources,
×
425
            builtin_modules,
×
426
        )
427
    }
428

429
    pub(crate) fn default_without_kernel(
5✔
430
        sources: Sources,
431
        builtin_modules: ModuleContainer,
432
    ) -> Compiler {
433
        Compiler::new(
434
            SymbolMap::new(),
5✔
435
            ConstantMap::new(),
5✔
436
            FxHashMap::default(),
5✔
437
            ModuleManager::default(),
5✔
438
            sources,
5✔
439
            builtin_modules,
5✔
440
        )
441
    }
442

443
    /// Registers a name in the underlying symbol map and returns the idx that it maps to
444
    pub fn register(&mut self, name: &str) -> usize {
638✔
445
        let spur = name.into();
638✔
446
        self.symbol_map.add(&spur)
638✔
447
    }
448

449
    /// Get the index associated with a name in the underlying symbol map
450
    /// If the name hasn't been registered, this will return `None`
451
    pub fn get_idx(&self, name: &str) -> Option<usize> {
3,127✔
452
        self.symbol_map.get(&InternedString::try_get(name)?).ok()
6,254✔
453
    }
454

455
    pub fn register_builtin(&mut self, name: String, contents: String) {
×
456
        self.module_manager.add_builtin_module(name, contents);
×
457
    }
458

459
    pub fn add_search_directory(&mut self, dir: PathBuf) {
×
460
        self.search_dirs.push(dir);
×
461
    }
462

463
    pub fn compile_executable_from_expressions(
170✔
464
        &mut self,
465
        exprs: Vec<ExprKind>,
466
    ) -> Result<RawProgramWithSymbols> {
467
        self.compile_raw_program(exprs, None)
170✔
468
    }
469

470
    pub fn compile_executable<E: AsRef<str> + Into<Cow<'static, str>>>(
635✔
471
        &mut self,
472
        expr_str: E,
473
        path: Option<PathBuf>,
474
    ) -> Result<RawProgramWithSymbols> {
475
        #[cfg(feature = "profiling")]
476
        let now = Instant::now();
635✔
477

478
        let expr_str = expr_str.into();
635✔
479

480
        let id = self.sources.add_source(expr_str.clone(), path.clone());
635✔
481

482
        // Could fail here
483
        let parsed: std::result::Result<Vec<ExprKind>, ParseError> = path
635✔
484
            .as_ref()
485
            .map(|p| Parser::new_from_source(expr_str.as_ref(), p.clone(), Some(id)))
1,281✔
486
            .unwrap_or_else(|| Parser::new(expr_str.as_ref(), Some(id)))
1,894✔
487
            .without_lowering()
488
            .map(|x| x.and_then(lower_macro_and_require_definitions))
10,724✔
489
            .collect();
490

491
        #[cfg(feature = "profiling")]
635✔
492
        if log::log_enabled!(target: "pipeline_time", log::Level::Debug) {
635✔
493
            log::debug!(target: "pipeline_time", "Parsing Time: {:?}", now.elapsed());
635✔
494
        }
495

496
        // TODO fix this hack
497
        self.compile_raw_program(parsed?, path)
1,270✔
498
    }
499

500
    // TODO: Add a flag/function for parsing comments as well
501
    // Move the body of this function into the other one, so that way we have proper
502
    pub fn emit_expanded_ast(
×
503
        &mut self,
504
        expr_str: &str,
505
        path: Option<PathBuf>,
506
    ) -> Result<Vec<ExprKind>> {
507
        let id = self.sources.add_source(expr_str.to_string(), path.clone());
×
508

509
        // Could fail here
510
        let parsed: std::result::Result<Vec<ExprKind>, ParseError> =
×
511
            Parser::new(expr_str, Some(id))
×
512
                .without_lowering()
513
                .map(|x| x.and_then(lower_macro_and_require_definitions))
×
514
                .collect();
515

516
        let parsed = parsed?;
×
517

518
        self.lower_expressions_impl(parsed, path)
519
    }
520

521
    pub fn emit_expanded_ast_without_optimizations(
1✔
522
        &mut self,
523
        expr_str: &str,
524
        path: Option<PathBuf>,
525
    ) -> Result<Vec<ExprKind>> {
526
        let id = self.sources.add_source(expr_str.to_string(), path.clone());
1✔
527

528
        // Could fail here
529
        let parsed: std::result::Result<Vec<ExprKind>, ParseError> =
1✔
530
            Parser::new(expr_str, Some(id))
1✔
531
                .without_lowering()
532
                .map(|x| x.and_then(lower_macro_and_require_definitions))
3✔
533
                .collect();
534

535
        let parsed = parsed?;
2✔
536

537
        self.expand_ast(parsed, path)
538
    }
539

540
    pub fn compile_module(
×
541
        &mut self,
542
        path: PathBuf,
543
        sources: &mut Sources,
544
        builtin_modules: ModuleContainer,
545
    ) -> Result<()> {
546
        self.module_manager.add_module(
×
547
            path,
×
548
            &mut self.macro_env,
×
549
            &mut self.kernel,
×
550
            sources,
×
551
            builtin_modules,
×
552
        )
553
    }
554

555
    pub fn modules(&self) -> &FxHashMap<PathBuf, CompiledModule> {
37,970✔
556
        self.module_manager.modules()
37,970✔
557
    }
558

559
    pub fn expand_expressions(
803✔
560
        &mut self,
561
        exprs: Vec<ExprKind>,
562
        path: Option<PathBuf>,
563
    ) -> Result<Vec<ExprKind>> {
564
        // #[cfg(feature = "modules")]
565
        return self.module_manager.compile_main(
803✔
566
            &mut self.macro_env,
803✔
567
            &mut self.kernel,
803✔
568
            &mut self.sources,
803✔
569
            exprs,
803✔
570
            path,
803✔
571
            self.builtin_modules.clone(),
803✔
572
            &mut self.lifted_kernel_environments,
803✔
573
            &mut self.lifted_macro_environments,
803✔
574
            &self.search_dirs,
803✔
575
        );
576

577
        // #[cfg(not(feature = "modules"))]
578
        // self.module_manager
579
        //     .expand_expressions(&mut self.macro_env, exprs)
580
    }
581

582
    fn generate_instructions_for_executable(
779✔
583
        &mut self,
584
        expanded_statements: Vec<ExprKind>,
585
    ) -> Result<Vec<Vec<Instruction>>> {
586
        let mut results = Vec::with_capacity(expanded_statements.len());
779✔
587
        // let mut instruction_buffer = Vec::new();
588
        // let mut index_buffer = Vec::new();
589

590
        let analysis = {
779✔
591
            let mut analysis = std::mem::take(&mut self.analysis);
779✔
592

593
            analysis.fresh_from_exprs(&expanded_statements);
779✔
594
            analysis.populate_captures_twice(&expanded_statements);
779✔
595

596
            // let mut analysis = Analysis::from_exprs(&expanded_statements);
597
            // analysis.populate_captures(&expanded_statements);
598
            // analysis.populate_captures(&expanded_statements);
599
            analysis
779✔
600
        };
601

602
        for expr in expanded_statements {
49,269✔
603
            let instructions =
24,245✔
604
                super::code_gen::CodeGenerator::new(&mut self.constant_map, &analysis)
605
                    .top_level_compile(&expr)?;
×
606

607
            results.push(instructions);
24,245✔
608
        }
609

610
        // This... cannot be efficient?
611
        // for idx in index_buffer {
612
        //     let extracted: Vec<Instruction> = instruction_buffer.drain(0..idx).collect();
613
        //     results.push(extracted);
614
        // }
615

616
        self.analysis = analysis;
779✔
617

618
        Ok(results)
779✔
619
    }
620

621
    fn expand_ast(&mut self, exprs: Vec<ExprKind>, path: Option<PathBuf>) -> Result<Vec<ExprKind>> {
1✔
622
        let mut expanded_statements = self.expand_expressions(exprs, path)?;
2✔
623

624
        log::debug!(target: "expansion-phase", "Expanding macros -> phase 1");
×
625

626
        if let Some(kernel) = self.kernel.as_mut() {
2✔
627
            // Label anything at the top as well - top level
628
            kernel.load_syntax_transformers(&mut expanded_statements, "top-level".to_string())?;
×
629
        }
630

631
        for expr in expanded_statements.iter_mut() {
2✔
632
            expand_kernel_in_env(
633
                expr,
1✔
634
                self.kernel.as_mut(),
1✔
635
                self.builtin_modules.clone(),
1✔
636
                "top-level",
637
            )?;
638

639
            crate::parser::expand_visitor::expand(expr, &self.macro_env)?;
1✔
640
        }
641

642
        for expr in expanded_statements.iter_mut() {
2✔
643
            expand_kernel_in_env(
644
                expr,
1✔
645
                self.kernel.as_mut(),
1✔
646
                self.builtin_modules.clone(),
1✔
647
                "top-level",
648
            )?;
649
            crate::parser::expand_visitor::expand(expr, &self.macro_env)?;
1✔
650
            lower_entire_ast(expr)?;
1✔
651

652
            for module in &self.lifted_macro_environments {
11✔
653
                if let Some(macro_env) = self.modules().get(module).map(|x| &x.macro_map) {
10✔
654
                    let source_id = self.sources.get_source_id(module).unwrap();
655

656
                    // println!("Expanding macros from: {:?}", module);
657

658
                    crate::parser::expand_visitor::expand_with_source_id(
659
                        expr,
660
                        macro_env,
661
                        Some(source_id),
662
                    )?
663

664
                    // crate::parser::expand_visitor::expand(expr, macro_env)?
665
                }
666
            }
667

668
            // Lift all of the kernel macros as well?
669
            for (module, lifted_env) in &mut self.lifted_kernel_environments {
3✔
670
                let changed = expand_kernel_in_env_with_change(
671
                    expr,
672
                    self.kernel.as_mut(),
673
                    self.builtin_modules.clone(),
674
                    &module,
675
                )?;
676

677
                if changed {
×
678
                    lifted_env.name_mangler.visit(expr);
×
679
                }
680
            }
681

682
            expand_kernel_in_env(
683
                expr,
1✔
684
                self.kernel.as_mut(),
1✔
685
                self.builtin_modules.clone(),
1✔
686
                "top-level",
687
            )?;
688

689
            // TODO: If we have this, then we have to lower all of the expressions again
690
            crate::parser::expand_visitor::expand(expr, &self.macro_env)?;
1✔
691

692
            // for expr in expanded_statements.iter_mut() {
693
            lower_entire_ast(expr)?;
1✔
694
        }
695

696
        // @Matt 11/15/2024
697
        // TODO: At this point, we'll want to remove
698
        // any remaining provide statements.
699

700
        log::debug!(target: "expansion-phase", "Beginning constant folding");
1✔
701

702
        // Remove remaining provides from the top level.
703

704
        // let mut expanded_statements =
705
        // self.apply_const_evaluation(constants.clone(), expanded_statements, false)?;
706

707
        // expanded_statements.pretty_print();
708

709
        self.shadowed_variable_renamer
1✔
710
            .rename_shadowed_variables(&mut expanded_statements, true);
1✔
711

712
        let mut analysis = std::mem::take(&mut self.analysis);
1✔
713
        analysis.fresh_from_exprs(&expanded_statements);
1✔
714
        analysis.populate_captures(&expanded_statements);
1✔
715

716
        let mut semantic = SemanticAnalysis::from_analysis(&mut expanded_statements, analysis);
1✔
717

718
        // This is definitely broken still
719
        semantic
1✔
720
            .elide_single_argument_lambda_applications()
721
            .replace_non_shadowed_globals_with_builtins(
722
                &mut self.macro_env,
1✔
723
                &mut self.module_manager,
1✔
724
                &mut self.mangled_identifiers,
1✔
725
            )
726
            // TODO: To get this to work, we have to check the macros to make sure those
727
            // are safe to eliminate. In interactive mode, we'll
728
            // be unable to optimize those away
729
            .remove_unused_globals_with_prefix(
730
                MANGLER_PREFIX,
1✔
731
                &self.macro_env,
1✔
732
                &self.module_manager,
1✔
733
            );
734
        // Don't do lambda lifting here
735
        // .lift_pure_local_functions()
736
        // .lift_all_local_functions();
737

738
        // debug!("About to expand defines");
739

740
        log::debug!(target: "expansion-phase", "Flattening begins, converting internal defines to let expressions");
1✔
741

742
        let mut analysis = semantic.into_analysis();
1✔
743

744
        let expanded_statements = flatten_begins_and_expand_defines(expanded_statements)?;
1✔
745

746
        let mut expanded_statements = filter_provides(expanded_statements);
747

748
        // After define expansion, we'll want this
749
        // RenameShadowedVariables::rename_shadowed_vars(&mut expanded_statements);
750

751
        self.shadowed_variable_renamer
752
            .rename_shadowed_variables(&mut expanded_statements, false);
753

754
        analysis.fresh_from_exprs(&expanded_statements);
755
        analysis.populate_captures(&expanded_statements);
756

757
        let mut semantic = SemanticAnalysis::from_analysis(&mut expanded_statements, analysis);
758

759
        semantic.check_if_values_are_redefined()?;
×
760

761
        semantic.refresh_variables();
1✔
762
        semantic.flatten_anonymous_functions();
1✔
763
        semantic.refresh_variables();
1✔
764

765
        // Replace mutation with boxes
766

767
        semantic.populate_captures_twice();
1✔
768

769
        semantic.replace_mutable_captured_variables_with_boxes();
1✔
770

771
        log::debug!(target: "expansion-phase", "Expanding multiple arity functions");
1✔
772

773
        let mut analysis = semantic.into_analysis();
1✔
774

775
        // Rename them again
776
        // RenameShadowedVariables::rename_shadowed_vars(&mut expanded_statements);
777

778
        self.shadowed_variable_renamer
1✔
779
            .rename_shadowed_variables(&mut expanded_statements, false);
1✔
780

781
        // let mut expanded_statements =
782
        // log::info!(target: "expansion-phase", "Aggressive constant evaluation with memoization");
783

784
        // Begin lowering anonymous function calls to lets
785

786
        analysis.fresh_from_exprs(&expanded_statements);
1✔
787
        analysis.populate_captures(&expanded_statements);
1✔
788
        let mut semantic = SemanticAnalysis::from_analysis(&mut expanded_statements, analysis);
1✔
789

790
        semantic.replace_anonymous_function_calls_with_plain_lets();
1✔
791

792
        self.analysis = semantic.into_analysis();
1✔
793

794
        Ok(expanded_statements)
1✔
795
    }
796

797
    pub(crate) fn lower_expressions_impl(
802✔
798
        &mut self,
799
        exprs: Vec<ExprKind>,
800
        path: Option<PathBuf>,
801
    ) -> Result<Vec<ExprKind>> {
802
        #[cfg(feature = "profiling")]
803
        let now = Instant::now();
804

805
        // println!("Before expanding macros");
806
        // exprs.pretty_print();
807

808
        let mut expanded_statements = self.expand_expressions(exprs, path)?;
1,604✔
809

810
        // println!("After expanding macros");
811
        // expanded_statements.pretty_print();
812

813
        #[cfg(feature = "profiling")]
814
        log::debug!(target: "pipeline_time", "Phase 1 module expansion time: {:?}", now.elapsed());
815

816
        #[cfg(feature = "profiling")]
817
        let now = Instant::now();
818

819
        log::debug!(target: "expansion-phase", "Expanding macros -> phase 1");
×
820

821
        if let Some(kernel) = self.kernel.as_mut() {
1,242✔
822
            // Label anything at the top as well - top level
823
            kernel.load_syntax_transformers(&mut expanded_statements, "top-level".to_string())?;
×
824
        }
825

826
        for expr in expanded_statements.iter_mut() {
22,525✔
827
            expand_kernel_in_env(
828
                expr,
21,736✔
829
                self.kernel.as_mut(),
21,736✔
830
                self.builtin_modules.clone(),
21,736✔
831
                "top-level",
832
            )?;
833
            crate::parser::expand_visitor::expand(expr, &self.macro_env)?;
21,735✔
834
            lower_entire_ast(expr)?;
21,744✔
835

836
            for module in &self.lifted_macro_environments {
205,304✔
837
                if let Some(macro_env) = self.modules().get(module).map(|x| &x.macro_map) {
183,578✔
838
                    let source_id = self.sources.get_source_id(module).unwrap();
839

840
                    crate::parser::expand_visitor::expand_with_source_id(
841
                        expr,
842
                        macro_env,
843
                        Some(source_id),
844
                    )?;
845
                }
846
            }
847

848
            // Lift all of the kernel macros as well?
849
            for (module, lifted_env) in &mut self.lifted_kernel_environments {
57,150✔
850
                let changed = expand_kernel_in_env_with_change(
851
                    expr,
852
                    self.kernel.as_mut(),
853
                    self.builtin_modules.clone(),
854
                    &module,
855
                )?;
856

857
                if changed {
×
858
                    lifted_env.name_mangler.visit(expr);
×
859
                }
860
            }
861

862
            expand_kernel_in_env(
863
                expr,
21,726✔
864
                self.kernel.as_mut(),
21,726✔
865
                self.builtin_modules.clone(),
21,726✔
866
                "top-level",
867
            )?;
868

869
            // TODO: If we have this, then we have to lower all of the expressions again
870
            crate::parser::expand_visitor::expand(expr, &self.macro_env)?;
21,726✔
871

872
            // for expr in expanded_statements.iter_mut() {
873
            lower_entire_ast(expr)?;
21,726✔
874
        }
875

876
        // TODO: Check that defines are in legal positions, post expansion.
877

878
        #[cfg(feature = "profiling")]
879
        log::debug!(target: "pipeline_time", "Top level macro expansion time: {:?}", now.elapsed());
880

881
        log::debug!(target: "expansion-phase", "Beginning constant folding");
779✔
882

883
        // steel_parser::ast::AstTools::pretty_print(&expanded_statements);
884

885
        // println!(
886
        //     "Modules: {:#?}",
887
        //     self.module_manager.modules().keys().collect::<Vec<_>>()
888
        // );
889

890
        // self.sources.debug_sources();
891

892
        let expanded_statements =
779✔
893
            self.apply_const_evaluation(constant_primitives(), expanded_statements, false)?;
779✔
894

895
        let expanded_statements = flatten_begins_and_expand_defines(expanded_statements)?;
779✔
896

897
        // TODO: Move this to its own function
898
        let mut expanded_statements = filter_provides(expanded_statements);
899

900
        #[cfg(feature = "profiling")]
901
        let now = Instant::now();
902

903
        // println!("--- Before renaming: ----");
904
        // expanded_statements.pretty_print();
905

906
        // TODO: Probably lift this above the const evaluation anyway
907
        self.shadowed_variable_renamer
908
            .rename_shadowed_variables(&mut expanded_statements, true);
909

910
        // println!("--- After renaming: ----");
911
        // expanded_statements.pretty_print();
912

913
        // let mut expanded_statements =
914
        //     self.apply_const_evaluation(constants.clone(), expanded_statements, false)?;
915

916
        let mut analysis = std::mem::take(&mut self.analysis);
917

918
        // Pre populate the analysis here
919
        analysis.fresh_from_exprs(&expanded_statements);
920

921
        // let mut analysis = Analysis::from_exprs(&expanded_statements);
922
        analysis.populate_captures(&expanded_statements);
923

924
        let mut semantic = SemanticAnalysis::from_analysis(&mut expanded_statements, analysis);
925

926
        // This is definitely broken still
927
        semantic
928
            .elide_single_argument_lambda_applications()
929
            .replace_non_shadowed_globals_with_builtins(
930
                &mut self.macro_env,
931
                &mut self.module_manager,
932
                &mut self.mangled_identifiers,
933
            )
934
            // TODO: To get this to work, we have to check the macros to make sure those
935
            // are safe to eliminate. In interactive mode, we'll
936
            // be unable to optimize those away
937
            .remove_unused_globals_with_prefix(
938
                MANGLER_PREFIX,
939
                &self.macro_env,
940
                &self.module_manager,
941
            )
942
            .lift_pure_local_functions()
943
            .lift_all_local_functions();
944

945
        // debug!("About to expand defines");
946

947
        log::debug!(target: "expansion-phase", "Flattening begins, converting internal defines to let expressions");
×
948

949
        let mut analysis = semantic.into_analysis();
779✔
950

951
        let mut expanded_statements = flatten_begins_and_expand_defines(expanded_statements)?;
779✔
952

953
        self.shadowed_variable_renamer
954
            .rename_shadowed_variables(&mut expanded_statements, false);
955

956
        // After define expansion, we'll want this
957
        // RenameShadowedVariables::rename_shadowed_vars(&mut expanded_statements);
958

959
        analysis.fresh_from_exprs(&expanded_statements);
960
        analysis.populate_captures(&expanded_statements);
961

962
        // expanded_statements.pretty_print();
963

964
        let mut semantic = SemanticAnalysis::from_analysis(&mut expanded_statements, analysis);
965

966
        // Check if the values are redefined!
967
        semantic.check_if_values_are_redefined()?;
×
968

969
        // semantic.refresh_variables();
970
        semantic.flatten_anonymous_functions();
779✔
971
        semantic.refresh_variables();
779✔
972

973
        // Replace mutation with boxes
974
        semantic.populate_captures();
779✔
975
        semantic.populate_captures();
779✔
976

977
        semantic.replace_mutable_captured_variables_with_boxes();
779✔
978

979
        log::debug!(target: "expansion-phase", "Expanding multiple arity functions");
779✔
980

981
        let mut analysis = semantic.into_analysis();
779✔
982

983
        // Rename them again
984
        // RenameShadowedVariables::rename_shadowed_vars(&mut expanded_statements);
985
        self.shadowed_variable_renamer
779✔
986
            .rename_shadowed_variables(&mut expanded_statements, false);
779✔
987

988
        // TODO - make sure I want to keep this
989
        // let mut expanded_statements =
990

991
        // log::info!(target: "expansion-phase", "Aggressive constant evaluation with memoization");
992

993
        // Begin lowering anonymous function calls to lets
994
        analysis.fresh_from_exprs(&expanded_statements);
779✔
995
        analysis.populate_captures(&expanded_statements);
779✔
996
        let mut semantic = SemanticAnalysis::from_analysis(&mut expanded_statements, analysis);
779✔
997
        // semantic.populate_captures();
998

999
        semantic.replace_anonymous_function_calls_with_plain_lets();
779✔
1000

1001
        #[cfg(feature = "profiling")]
1002
        log::info!(target: "pipeline_time", "CAT time: {:?}", now.elapsed());
779✔
1003

1004
        self.analysis = semantic.into_analysis();
779✔
1005

1006
        // We don't want to leave this allocate memory just hanging around, but leave enough for
1007
        // interactive usages
1008
        self.analysis.shrink_capacity();
779✔
1009

1010
        // steel_parser::ast::AstTools::pretty_print(&expanded_statements);
1011

1012
        Ok(expanded_statements)
779✔
1013

1014
        // Done lowering anonymous function calls to let
1015
        // TODO: Re-enable this, but not in the repl. This repl causes... issues with the implementation
1016
        // self.apply_const_evaluation(constant_primitives(), expanded_statements, true)
1017
    }
1018

1019
    // TODO
1020
    // figure out how the symbols will work so that a raw program with symbols
1021
    // can be later pulled in and symbols can be interned correctly
1022
    fn compile_raw_program(
802✔
1023
        &mut self,
1024
        exprs: Vec<ExprKind>,
1025
        path: Option<PathBuf>,
1026
    ) -> Result<RawProgramWithSymbols> {
1027
        log::debug!(target: "expansion-phase", "Expanding macros -> phase 0");
802✔
1028

1029
        let expanded_statements = self.lower_expressions_impl(exprs, path)?;
1,604✔
1030

1031
        // println!("--- Final AST ---");
1032
        // println!("");
1033
        // steel_parser::ast::AstTools::pretty_print(&expanded_statements);
1034

1035
        // log::info!(
1036
        //     "{}",
1037
        //     expanded_statements
1038
        //         .iter()
1039
        //         .map(|x| x.to_pretty(60))
1040
        //         .join("\n\n")
1041
        // );
1042

UNCOV
1043
        log::debug!(target: "expansion-phase", "Generating instructions");
×
1044

1045
        let instructions = self.generate_instructions_for_executable(expanded_statements)?;
1,558✔
1046

1047
        let mut raw_program = RawProgramWithSymbols::new(
1048
            instructions,
779✔
1049
            self.constant_map.clone(),
779✔
1050
            "0.1.0".to_string(),
779✔
1051
        );
1052

1053
        // Make sure to apply the peephole optimizations
1054
        raw_program.apply_optimizations();
779✔
1055

1056
        // Lets see everything that gets run!
1057
        // raw_program.debug_print_log();
1058

1059
        Ok(raw_program)
779✔
1060
    }
1061

1062
    fn _run_const_evaluation_with_memoization(
×
1063
        &mut self,
1064
        mut _expanded_statements: Vec<ExprKind>,
1065
    ) -> Result<Vec<ExprKind>> {
1066
        todo!("Implement kernel level const evaluation here!")
1067
    }
1068

1069
    fn apply_const_evaluation(
779✔
1070
        &mut self,
1071
        constants: ImmutableHashMap<InternedString, SteelVal, FxBuildHasher>,
1072
        mut expanded_statements: Vec<ExprKind>,
1073
        use_kernel: bool,
1074
    ) -> Result<Vec<ExprKind>> {
1075
        #[cfg(feature = "profiling")]
1076
        let opt_time = Instant::now();
779✔
1077

1078
        let mut maybe_kernel = None;
779✔
1079

1080
        if use_kernel {
779✔
1081
            if let Some(kernel) = self.kernel.as_mut() {
×
1082
                kernel.load_program_for_comptime(constants.clone(), &mut expanded_statements)?;
×
1083
            }
1084
        }
1085

1086
        let mut manager = ConstantEvaluatorManager::new(
1087
            &mut self.memoization_table,
1088
            constants.clone(),
1089
            self.opt_level,
1090
            if use_kernel {
1091
                &mut self.kernel
×
1092
            } else {
1093
                &mut maybe_kernel
779✔
1094
            },
1095
        );
1096

1097
        match self.opt_level {
1098
            // TODO
1099
            // Cut this off at 10 iterations no matter what
1100
            OptLevel::Three => {
1101
                // for _ in 0..10 {
1102
                expanded_statements = manager.run(expanded_statements)?;
1,558✔
1103

1104
                // if !manager.changed {
1105
                //     break;
1106
                // }
1107

1108
                // manager.changed = false;
1109
                // }
1110
            }
1111
            OptLevel::Two => {
1112
                expanded_statements = ConstantEvaluatorManager::new(
×
1113
                    &mut self.memoization_table,
×
1114
                    constants,
×
1115
                    self.opt_level,
×
1116
                    if use_kernel {
×
1117
                        &mut self.kernel
×
1118
                    } else {
1119
                        &mut maybe_kernel
×
1120
                    },
1121
                )
1122
                .run(expanded_statements)?;
×
1123
            }
1124
            _ => {}
×
1125
        }
1126

1127
        #[cfg(feature = "profiling")]
1128
        if log::log_enabled!(target: "pipeline_time", log::Level::Debug) {
1129
            log::debug!(
1130
                target: "pipeline_time",
1131
                "Const Evaluation Time: {:?}",
1132
                opt_time.elapsed()
1133
            );
1134
        };
1135

1136
        Ok(expanded_statements)
779✔
1137
    }
1138
}
1139

1140
fn filter_provides(expanded_statements: Vec<ExprKind>) -> Vec<ExprKind> {
780✔
1141
    expanded_statements
780✔
1142
        .into_iter()
1143
        .filter_map(|expr| match expr {
22,507✔
1144
            ExprKind::Begin(mut b) => {
2,171✔
1145
                let exprs = std::mem::take(&mut b.exprs);
2,171✔
1146
                b.exprs = exprs
2,171✔
1147
                    .into_iter()
2,171✔
1148
                    .filter_map(|e| match e {
230,318✔
1149
                        ExprKind::List(l) => {
2,499✔
1150
                            if l.first_ident().copied() == Some(*PROVIDE) {
2,499✔
1151
                                return None;
4✔
1152
                            }
1153
                            return Some(ExprKind::List(l));
2,495✔
1154
                        }
1155
                        other => return Some(other),
225,648✔
1156
                    })
1157
                    .collect();
1158

1159
                Some(ExprKind::Begin(b))
1160
            }
1161
            ExprKind::List(l) => {
1,061✔
1162
                if l.first_ident().copied() == Some(*PROVIDE) {
1,061✔
NEW
1163
                    return None;
×
1164
                }
1165
                return Some(ExprKind::List(l));
1,061✔
1166
            }
1167
            other => Some(other),
18,495✔
1168
        })
1169
        .collect()
1170
}
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