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

mattwparas / steel / 11947392359

21 Nov 2024 05:44AM CUT coverage: 46.072% (-0.2%) from 46.234%
11947392359

Pull #291

github

web-flow
Merge 8d943de47 into 9a2bb2ef5
Pull Request #291: Add rustls library and fix free identifier error message

16 of 86 new or added lines in 6 files covered. (18.6%)

37 existing lines in 10 files now uncovered.

12334 of 26771 relevant lines covered (46.07%)

415077.16 hits per line

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

76.67
/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::{
12
        instructions::{disassemble, u24},
13
        labels::Expr,
14
    },
15
    parser::{
16
        expand_visitor::{expand_kernel_in_env, expand_kernel_in_env_with_change},
17
        interner::InternedString,
18
        kernel::Kernel,
19
        parser::{lower_entire_ast, lower_macro_and_require_definitions},
20
    },
21
    steel_vm::{cache::MemoizationTable, engine::ModuleContainer, primitives::constant_primitives},
22
};
23
use crate::{
24
    core::{instructions::Instruction, opcode::OpCode},
25
    parser::parser::Sources,
26
};
27

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

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

39
use crate::rvals::{Result, SteelVal};
40

41
use crate::parser::ast::ExprKind;
42
use crate::parser::expander::SteelMacro;
43
use crate::parser::parser::SyntaxObject;
44
use crate::parser::parser::{ParseError, Parser};
45
use crate::parser::tokens::TokenType;
46

47
// use crate::core::instructions::{densify, DenseInstruction};
48

49
use crate::stop;
50

51
use crate::steel_vm::const_evaluation::ConstantEvaluatorManager;
52

53
use super::{
54
    constants::SerializableConstantMap,
55
    modules::{CompiledModule, ModuleManager},
56
    passes::{analysis::Analysis, mangle::NameMangler},
57
    program::RawProgramWithSymbols,
58
};
59

60
use crate::values::HashMap as ImmutableHashMap;
61

62
#[cfg(feature = "profiling")]
63
use std::time::Instant;
64

65
#[derive(Default)]
66
pub struct DebruijnIndicesInterner {
67
    flat_defines: HashSet<InternedString>,
68
    flat_defines_idx: HashSet<usize>,
69
    second_pass_defines: HashSet<InternedString>,
70
    second_pass_defines_idx: HashSet<usize>,
71
}
72

73
impl DebruijnIndicesInterner {
74
    pub fn collect_first_pass_defines(
24,231✔
75
        &mut self,
76
        instructions: &mut [Instruction],
77
        symbol_map: &mut SymbolMap,
78
    ) -> Result<()> {
79
        let mut flat_defines_non_closure: HashSet<InternedString> = HashSet::default();
24,231✔
80

81
        for i in 2..instructions.len() {
1,739,247✔
82
            match (&instructions[i], &instructions[i - 1], &instructions[i - 2]) {
1,715,016✔
83
                (
84
                    Instruction {
85
                        op_code: OpCode::BIND,
86
                        contents:
87
                            Some(Expr::Atom(SyntaxObject {
88
                                ty: TokenType::Identifier(s),
17,302✔
89
                                // span,
90
                                ..
17,302✔
91
                            })),
92
                        ..
17,302✔
93
                    },
94
                    Instruction {
17,302✔
95
                        op_code: OpCode::EDEF,
17,302✔
96
                        ..
17,302✔
97
                    },
98
                    Instruction {
17,302✔
99
                        op_code: OpCode::ECLOSURE,
17,302✔
100
                        ..
17,302✔
101
                    },
102
                ) => {
17,302✔
103
                    // Exiting a definition, clear it
104
                    flat_defines_non_closure.clear();
17,302✔
105

106
                    let idx = symbol_map.add(s);
17,302✔
107
                    self.flat_defines.insert(s.to_owned());
17,302✔
108

109
                    if let Some(x) = instructions.get_mut(i) {
34,604✔
110
                        x.payload_size = u24::from_usize(idx);
111
                    }
112
                }
113
                (
114
                    Instruction {
115
                        op_code: OpCode::BIND,
116
                        contents:
117
                            Some(Expr::Atom(SyntaxObject {
118
                                ty: TokenType::Identifier(s),
36,014✔
119
                                span,
36,014✔
120
                                ..
36,014✔
121
                            })),
122
                        ..
36,014✔
123
                    },
124
                    Instruction {
36,014✔
125
                        op_code: OpCode::EDEF,
36,014✔
126
                        ..
36,014✔
127
                    },
128
                    ..,
36,014✔
129
                ) => {
36,014✔
130
                    let idx = symbol_map.add(s);
36,014✔
131
                    self.flat_defines.insert(s.to_owned());
36,014✔
132

133
                    if flat_defines_non_closure.contains(s) {
36,014✔
NEW
134
                        stop!(BadSyntax => format!("Cannot reference identifier before its definition: {}", s.resolve()); *span);
×
135
                    }
136

137
                    flat_defines_non_closure.clear();
36,014✔
138

139
                    // self.flat_defines_idx.insert(idx);
140
                    // flat_defines_non_closure.insert(s.to_owned());
141

142
                    if let Some(x) = instructions.get_mut(i) {
36,014✔
143
                        x.payload_size = u24::from_usize(idx);
144
                    }
145
                }
146
                // (
147
                //     Instruction {
148
                //         op_code: OpCode::EDEF,
149
                //         ..
150
                //     },
151
                //     ..,
152
                // ) => {
153
                //     flat_defines_non_closure.clear();
154
                // }
155
                (
156
                    Instruction {
157
                        op_code: OpCode::CALLGLOBAL,
158
                        contents:
159
                            Some(Expr::Atom(SyntaxObject {
160
                                ty: TokenType::Identifier(s),
237,184✔
161
                                span,
237,184✔
162
                                ..
237,184✔
163
                            })),
164
                        ..
237,184✔
165
                    },
166
                    ..,
237,184✔
167
                ) => {
237,184✔
168
                    // We're referencing things within the scope
169
                    flat_defines_non_closure.insert(*s);
237,184✔
170

171
                    // let idx = symbol_map.add(s);
172
                    // self.flat_defines.insert(s.to_owned());
173

174
                    // self.flat_defines_idx.insert(idx);
175

176
                    // if flat_defines_non_closure.contains(s) {
177
                    //     println!("{}", disassemble(instructions));
178
                    //     stop!(BadSyntax => format!("Cannot reference identifier before definition: {}", s.resolve()); *span);
179
                    // }
180
                }
181
                _ => {}
1,424,516✔
182
            }
183
        }
184

185
        Ok(())
24,231✔
186
    }
187

188
    pub fn collect_second_pass_defines(
24,227✔
189
        &mut self,
190
        instructions: &mut [Instruction],
191
        symbol_map: &mut SymbolMap,
192
    ) -> Result<()> {
193
        // let mut second_pass_defines: HashSet<String> = HashSet::new();
194

195
        let mut depth = 0;
24,227✔
196

197
        // name mangle
198
        // Replace all identifiers with indices
199
        for i in 0..instructions.len() {
1,787,610✔
200
            match &instructions[i] {
1,763,383✔
201
                Instruction {
202
                    op_code: OpCode::SCLOSURE | OpCode::NEWSCLOSURE | OpCode::PUREFUNC,
203
                    ..
204
                } => {
37,692✔
205
                    depth += 1;
37,692✔
206
                }
207
                Instruction {
208
                    op_code: OpCode::ECLOSURE,
209
                    ..
210
                } => {
37,692✔
211
                    depth -= 1;
37,692✔
212
                }
213
                Instruction {
214
                    op_code: OpCode::BEGINSCOPE,
215
                    ..
216
                } => {
24,607✔
217
                    depth += 1;
24,607✔
218
                }
219
                Instruction {
220
                    op_code: OpCode::LETENDSCOPE,
221
                    ..
222
                } => {
24,605✔
223
                    depth -= 1;
24,605✔
224
                }
225
                Instruction {
226
                    op_code: OpCode::BIND,
227
                    contents:
228
                        Some(Expr::Atom(SyntaxObject {
229
                            ty: TokenType::Identifier(s),
53,312✔
230
                            ..
53,312✔
231
                        })),
232
                    ..
53,312✔
233
                } => {
53,312✔
234
                    // Keep track of where the defines actually are in the process
235
                    self.second_pass_defines.insert(s.to_owned());
53,312✔
236

237
                    // let idx = symbol_map.get(s).unwrap();
238

239
                    // self.second_pass_defines_idx.insert(idx);
240
                }
241
                Instruction {
242
                    op_code: OpCode::PUSH,
243
                    contents:
244
                        Some(Expr::Atom(SyntaxObject {
245
                            ty: TokenType::Identifier(s),
53,266✔
246
                            span,
53,266✔
247
                            ..
248
                        })),
249
                    ..
250
                }
251
                | Instruction {
252
                    op_code: OpCode::SET,
253
                    contents:
254
                        Some(Expr::Atom(SyntaxObject {
255
                            ty: TokenType::Identifier(s),
9,010✔
256
                            span,
9,010✔
257
                            ..
258
                        })),
259
                    ..
260
                } => {
261
                    if self.flat_defines.get(s).is_some()
62,276✔
262
                        && self.second_pass_defines.get(s).is_none()
47,676✔
263
                        && depth == 0
203✔
264
                    {
265
                        let message =
1✔
266
                            format!("Cannot reference an identifier before its definition: {s}");
1✔
267
                        stop!(FreeIdentifier => message; *span);
1✔
268
                    }
269

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

272
                    // TODO commenting this for now
273
                    if let Some(x) = instructions.get_mut(i) {
62,261✔
274
                        x.payload_size = u24::from_usize(idx);
275
                    }
276
                }
277
                Instruction {
278
                    op_code: OpCode::CALLGLOBAL,
279
                    contents:
280
                        Some(Expr::Atom(SyntaxObject {
281
                            ty: TokenType::Identifier(s),
238,238✔
282
                            span,
238,238✔
283
                            ..
284
                        })),
285
                    ..
286
                }
287
                | Instruction {
288
                    op_code: OpCode::CALLGLOBALTAIL,
289
                    contents:
290
                        Some(Expr::Atom(SyntaxObject {
291
                            ty: TokenType::Identifier(s),
38,074✔
292
                            span,
38,074✔
293
                            ..
294
                        })),
295
                    ..
296
                } => {
297
                    if self.flat_defines.get(s).is_some()
276,312✔
298
                        && self.second_pass_defines.get(s).is_none()
61,607✔
299
                        && depth == 0
11,071✔
300
                    {
301
                        let message =
1✔
302
                            format!("Cannot reference an identifier before its definition: {s}");
1✔
303
                        stop!(FreeIdentifier => message; *span);
1✔
304
                    }
305

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

308
                    // TODO commenting this for now
309
                    if let Some(x) = instructions.get_mut(i) {
276,305✔
310
                        x.payload_size = u24::from_usize(idx);
311
                    }
312
                }
313
                _ => {}
1,246,887✔
314
            }
315
        }
316

317
        Ok(())
24,205✔
318
    }
319
}
320

321
#[derive(Clone, Copy, PartialEq, PartialOrd, Serialize, Deserialize)]
322
pub enum OptLevel {
323
    Zero = 0,
324
    One,
325
    Two,
326
    Three,
327
}
328

329
#[derive(Clone)]
330
pub(crate) struct KernelDefMacroSpec {
331
    pub(crate) _env: String,
332
    pub(crate) _exported: Option<HashSet<InternedString>>,
333
    pub(crate) name_mangler: NameMangler,
334
}
335

336
#[derive(Clone)]
337
pub struct Compiler {
338
    pub(crate) symbol_map: SymbolMap,
339
    pub(crate) constant_map: ConstantMap,
340
    pub(crate) macro_env: FxHashMap<InternedString, SteelMacro>,
341
    module_manager: ModuleManager,
342
    opt_level: OptLevel,
343
    pub(crate) kernel: Option<Kernel>,
344
    memoization_table: MemoizationTable,
345
    mangled_identifiers: FxHashSet<InternedString>,
346
    // Try this out?
347
    lifted_kernel_environments: HashMap<String, KernelDefMacroSpec>,
348
    // Macros that... we need to compile against directly at the top level
349
    // This is really just a hack, but it solves cases for interactively
350
    // running at the top level using private macros.
351
    lifted_macro_environments: HashSet<PathBuf>,
352

353
    analysis: Analysis,
354
    shadowed_variable_renamer: RenameShadowedVariables,
355

356
    search_dirs: Vec<PathBuf>,
357

358
    // Include all the sources, module container, and constants
359
    // so that we can reference those at runtime. We probably should
360
    // just ignore the constants function in general. This unfortunately,
361
    // is under the hood, shared references to the engine, since we
362
    // want to have the compiler share everything with the runtime.
363
    sources: Sources,
364
    builtin_modules: ModuleContainer,
365
}
366

367
#[derive(Serialize, Deserialize)]
368
pub struct SerializableCompiler {
369
    pub(crate) symbol_map: SymbolMap,
370
    pub(crate) constant_map: SerializableConstantMap,
371
    pub(crate) macro_env: FxHashMap<InternedString, SteelMacro>,
372
    pub(crate) opt_level: OptLevel,
373
    pub(crate) module_manager: ModuleManager,
374
}
375

376
impl SerializableCompiler {
377
    #[allow(unused)]
378
    pub(crate) fn into_compiler(self) -> Compiler {
×
379
        let mut compiler = Compiler::default();
×
380

381
        compiler.symbol_map = self.symbol_map;
×
382
        compiler.constant_map = ConstantMap::from_serialized(self.constant_map).unwrap();
×
383
        compiler.macro_env = self.macro_env;
×
384
        compiler.opt_level = self.opt_level;
×
385
        compiler.module_manager = self.module_manager;
×
386

387
        compiler
×
388
    }
389
}
390

391
impl Compiler {
392
    #[allow(unused)]
393
    pub(crate) fn into_serializable_compiler(self) -> Result<SerializableCompiler> {
×
394
        Ok(SerializableCompiler {
×
395
            symbol_map: self.symbol_map,
×
396
            constant_map: self.constant_map.into_serializable_map(),
×
397
            macro_env: self.macro_env,
×
398
            opt_level: self.opt_level,
×
399
            module_manager: self.module_manager,
×
400
        })
401
    }
402
}
403

404
impl Default for Compiler {
405
    fn default() -> Self {
×
406
        Compiler::new(
407
            SymbolMap::new(),
×
408
            ConstantMap::new(),
×
409
            FxHashMap::default(),
×
410
            ModuleManager::default(),
×
411
            Sources::default(),
×
412
            ModuleContainer::default(),
×
413
        )
414
    }
415
}
416

417
impl Compiler {
418
    fn new(
5✔
419
        symbol_map: SymbolMap,
420
        constant_map: ConstantMap,
421
        macro_env: FxHashMap<InternedString, SteelMacro>,
422
        module_manager: ModuleManager,
423
        sources: Sources,
424
        builtin_modules: ModuleContainer,
425
    ) -> Compiler {
426
        Compiler {
427
            symbol_map,
428
            constant_map,
429
            macro_env,
430
            module_manager,
431
            opt_level: OptLevel::Three,
432
            kernel: None,
433
            memoization_table: MemoizationTable::new(),
5✔
434
            mangled_identifiers: FxHashSet::default(),
5✔
435
            lifted_kernel_environments: HashMap::new(),
5✔
436
            lifted_macro_environments: HashSet::new(),
5✔
437
            analysis: Analysis::pre_allocated(),
5✔
438
            shadowed_variable_renamer: RenameShadowedVariables::default(),
5✔
439
            search_dirs: Vec::new(),
5✔
440
            sources,
441
            builtin_modules,
442
        }
443
    }
444

445
    fn new_with_kernel(
×
446
        symbol_map: SymbolMap,
447
        constant_map: ConstantMap,
448
        macro_env: FxHashMap<InternedString, SteelMacro>,
449
        module_manager: ModuleManager,
450
        kernel: Kernel,
451
        sources: Sources,
452
        builtin_modules: ModuleContainer,
453
    ) -> Compiler {
454
        Compiler {
455
            symbol_map,
456
            constant_map,
457
            macro_env,
458
            module_manager,
459
            opt_level: OptLevel::Three,
460
            kernel: Some(kernel),
×
461
            memoization_table: MemoizationTable::new(),
×
462
            mangled_identifiers: FxHashSet::default(),
×
463
            lifted_kernel_environments: HashMap::new(),
×
464
            lifted_macro_environments: HashSet::new(),
×
465
            analysis: Analysis::pre_allocated(),
×
466
            shadowed_variable_renamer: RenameShadowedVariables::default(),
×
467
            search_dirs: Vec::new(),
×
468
            sources,
469
            builtin_modules,
470
        }
471
    }
472

473
    pub fn default_with_kernel(sources: Sources, builtin_modules: ModuleContainer) -> Compiler {
×
474
        Compiler::new_with_kernel(
475
            SymbolMap::new(),
×
476
            ConstantMap::new(),
×
477
            FxHashMap::default(),
×
478
            ModuleManager::default(),
×
479
            Kernel::new(),
×
480
            sources,
×
481
            builtin_modules,
×
482
        )
483
    }
484

485
    pub(crate) fn default_without_kernel(
5✔
486
        sources: Sources,
487
        builtin_modules: ModuleContainer,
488
    ) -> Compiler {
489
        Compiler::new(
490
            SymbolMap::new(),
5✔
491
            ConstantMap::new(),
5✔
492
            FxHashMap::default(),
5✔
493
            ModuleManager::default(),
5✔
494
            sources,
5✔
495
            builtin_modules,
5✔
496
        )
497
    }
498

499
    /// Registers a name in the underlying symbol map and returns the idx that it maps to
500
    pub fn register(&mut self, name: &str) -> usize {
638✔
501
        let spur = name.into();
638✔
502
        self.symbol_map.add(&spur)
638✔
503
    }
504

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

511
    pub fn register_builtin(&mut self, name: String, contents: String) {
×
512
        self.module_manager.add_builtin_module(name, contents);
×
513
    }
514

515
    pub fn add_search_directory(&mut self, dir: PathBuf) {
×
516
        self.search_dirs.push(dir);
×
517
    }
518

519
    pub fn compile_executable_from_expressions(
170✔
520
        &mut self,
521
        exprs: Vec<ExprKind>,
522
    ) -> Result<RawProgramWithSymbols> {
523
        self.compile_raw_program(exprs, None)
170✔
524
    }
525

526
    pub fn compile_executable<E: AsRef<str> + Into<Cow<'static, str>>>(
635✔
527
        &mut self,
528
        expr_str: E,
529
        path: Option<PathBuf>,
530
    ) -> Result<RawProgramWithSymbols> {
531
        #[cfg(feature = "profiling")]
532
        let now = Instant::now();
635✔
533

534
        let expr_str = expr_str.into();
635✔
535

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

538
        // Could fail here
539
        let parsed: std::result::Result<Vec<ExprKind>, ParseError> = path
635✔
540
            .as_ref()
541
            .map(|p| Parser::new_from_source(expr_str.as_ref(), p.clone(), Some(id)))
1,281✔
542
            .unwrap_or_else(|| Parser::new(expr_str.as_ref(), Some(id)))
1,894✔
543
            .without_lowering()
544
            .map(|x| x.and_then(lower_macro_and_require_definitions))
10,724✔
545
            .collect();
546

547
        #[cfg(feature = "profiling")]
635✔
548
        if log::log_enabled!(target: "pipeline_time", log::Level::Debug) {
635✔
549
            log::debug!(target: "pipeline_time", "Parsing Time: {:?}", now.elapsed());
635✔
550
        }
551

552
        // TODO fix this hack
553
        self.compile_raw_program(parsed?, path)
1,270✔
554
    }
555

556
    // TODO: Add a flag/function for parsing comments as well
557
    // Move the body of this function into the other one, so that way we have proper
558
    pub fn emit_expanded_ast(
×
559
        &mut self,
560
        expr_str: &str,
561
        path: Option<PathBuf>,
562
    ) -> Result<Vec<ExprKind>> {
563
        let id = self.sources.add_source(expr_str.to_string(), path.clone());
×
564

565
        // Could fail here
566
        let parsed: std::result::Result<Vec<ExprKind>, ParseError> =
×
567
            Parser::new(expr_str, Some(id))
×
568
                .without_lowering()
569
                .map(|x| x.and_then(lower_macro_and_require_definitions))
×
570
                .collect();
571

572
        let parsed = parsed?;
×
573

574
        self.lower_expressions_impl(parsed, path)
575
    }
576

577
    pub fn emit_expanded_ast_without_optimizations(
1✔
578
        &mut self,
579
        expr_str: &str,
580
        path: Option<PathBuf>,
581
    ) -> Result<Vec<ExprKind>> {
582
        let id = self.sources.add_source(expr_str.to_string(), path.clone());
1✔
583

584
        // Could fail here
585
        let parsed: std::result::Result<Vec<ExprKind>, ParseError> =
1✔
586
            Parser::new(expr_str, Some(id))
1✔
587
                .without_lowering()
588
                .map(|x| x.and_then(lower_macro_and_require_definitions))
3✔
589
                .collect();
590

591
        let parsed = parsed?;
2✔
592

593
        self.expand_ast(parsed, path)
594
    }
595

596
    pub fn compile_module(
×
597
        &mut self,
598
        path: PathBuf,
599
        sources: &mut Sources,
600
        builtin_modules: ModuleContainer,
601
    ) -> Result<()> {
602
        self.module_manager.add_module(
×
603
            path,
×
604
            &mut self.macro_env,
×
605
            &mut self.kernel,
×
606
            sources,
×
607
            builtin_modules,
×
608
        )
609
    }
610

611
    pub fn modules(&self) -> &FxHashMap<PathBuf, CompiledModule> {
53,369✔
612
        self.module_manager.modules()
53,369✔
613
    }
614

615
    pub fn expand_expressions(
803✔
616
        &mut self,
617
        exprs: Vec<ExprKind>,
618
        path: Option<PathBuf>,
619
    ) -> Result<Vec<ExprKind>> {
620
        // #[cfg(feature = "modules")]
621
        return self.module_manager.compile_main(
803✔
622
            &mut self.macro_env,
803✔
623
            &mut self.kernel,
803✔
624
            &mut self.sources,
803✔
625
            exprs,
803✔
626
            path,
803✔
627
            self.builtin_modules.clone(),
803✔
628
            &mut self.lifted_kernel_environments,
803✔
629
            &mut self.lifted_macro_environments,
803✔
630
            &self.search_dirs,
803✔
631
        );
632

633
        // #[cfg(not(feature = "modules"))]
634
        // self.module_manager
635
        //     .expand_expressions(&mut self.macro_env, exprs)
636
    }
637

638
    fn generate_instructions_for_executable(
779✔
639
        &mut self,
640
        expanded_statements: Vec<ExprKind>,
641
    ) -> Result<Vec<Vec<Instruction>>> {
642
        let mut results = Vec::with_capacity(expanded_statements.len());
779✔
643
        // let mut instruction_buffer = Vec::new();
644
        // let mut index_buffer = Vec::new();
645

646
        let analysis = {
779✔
647
            let mut analysis = std::mem::take(&mut self.analysis);
779✔
648

649
            analysis.fresh_from_exprs(&expanded_statements);
779✔
650
            analysis.populate_captures_twice(&expanded_statements);
779✔
651

652
            // let mut analysis = Analysis::from_exprs(&expanded_statements);
653
            // analysis.populate_captures(&expanded_statements);
654
            // analysis.populate_captures(&expanded_statements);
655
            analysis
779✔
656
        };
657

658
        for expr in expanded_statements {
49,269✔
659
            let instructions =
24,245✔
660
                super::code_gen::CodeGenerator::new(&mut self.constant_map, &analysis)
661
                    .top_level_compile(&expr)?;
×
662

663
            results.push(instructions);
24,245✔
664
        }
665

666
        // This... cannot be efficient?
667
        // for idx in index_buffer {
668
        //     let extracted: Vec<Instruction> = instruction_buffer.drain(0..idx).collect();
669
        //     results.push(extracted);
670
        // }
671

672
        self.analysis = analysis;
779✔
673

674
        Ok(results)
779✔
675
    }
676

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

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

682
        if let Some(kernel) = self.kernel.as_mut() {
2✔
683
            // Label anything at the top as well - top level
684
            kernel.load_syntax_transformers(&mut expanded_statements, "top-level".to_string())?;
×
685
        }
686

687
        for expr in expanded_statements.iter_mut() {
2✔
688
            expand_kernel_in_env(
689
                expr,
1✔
690
                self.kernel.as_mut(),
1✔
691
                self.builtin_modules.clone(),
1✔
692
                "top-level",
693
            )?;
694

695
            crate::parser::expand_visitor::expand(expr, &self.macro_env)?;
1✔
696
        }
697

698
        for expr in expanded_statements.iter_mut() {
2✔
699
            expand_kernel_in_env(
700
                expr,
1✔
701
                self.kernel.as_mut(),
1✔
702
                self.builtin_modules.clone(),
1✔
703
                "top-level",
704
            )?;
705
            crate::parser::expand_visitor::expand(expr, &self.macro_env)?;
1✔
706
            lower_entire_ast(expr)?;
1✔
707

708
            for module in &self.lifted_macro_environments {
11✔
709
                if let Some(macro_env) = self.modules().get(module).map(|x| &x.macro_map) {
10✔
710
                    let source_id = self.sources.get_source_id(module).unwrap();
711

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

714
                    crate::parser::expand_visitor::expand_with_source_id(
715
                        expr,
716
                        macro_env,
717
                        Some(source_id),
718
                    )?
719

720
                    // crate::parser::expand_visitor::expand(expr, macro_env)?
721
                }
722
            }
723

724
            // Lift all of the kernel macros as well?
725
            for (module, lifted_env) in &mut self.lifted_kernel_environments {
3✔
726
                let changed = expand_kernel_in_env_with_change(
727
                    expr,
728
                    self.kernel.as_mut(),
729
                    self.builtin_modules.clone(),
730
                    &module,
731
                )?;
732

733
                if changed {
×
734
                    lifted_env.name_mangler.visit(expr);
×
735
                }
736
            }
737

738
            expand_kernel_in_env(
739
                expr,
1✔
740
                self.kernel.as_mut(),
1✔
741
                self.builtin_modules.clone(),
1✔
742
                "top-level",
743
            )?;
744

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

748
            // for expr in expanded_statements.iter_mut() {
749
            lower_entire_ast(expr)?;
1✔
750
        }
751

752
        // @Matt 11/15/2024
753
        // TODO: At this point, we'll want to remove
754
        // any remaining provide statements.
755

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

758
        // Remove remaining provides from the top level.
759

760
        // let mut expanded_statements =
761
        // self.apply_const_evaluation(constants.clone(), expanded_statements, false)?;
762

763
        // expanded_statements.pretty_print();
764

765
        self.shadowed_variable_renamer
1✔
766
            .rename_shadowed_variables(&mut expanded_statements, true);
1✔
767

768
        let mut analysis = std::mem::take(&mut self.analysis);
1✔
769
        analysis.fresh_from_exprs(&expanded_statements);
1✔
770
        analysis.populate_captures(&expanded_statements);
1✔
771

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

774
        // This is definitely broken still
775
        semantic
1✔
776
            .elide_single_argument_lambda_applications()
777
            .replace_non_shadowed_globals_with_builtins(
778
                &mut self.macro_env,
1✔
779
                &mut self.module_manager,
1✔
780
                &mut self.mangled_identifiers,
1✔
781
            )
782
            // TODO: To get this to work, we have to check the macros to make sure those
783
            // are safe to eliminate. In interactive mode, we'll
784
            // be unable to optimize those away
785
            .remove_unused_globals_with_prefix(
786
                MANGLER_PREFIX,
1✔
787
                &self.macro_env,
1✔
788
                &self.module_manager,
1✔
789
            );
790
        // Don't do lambda lifting here
791
        // .lift_pure_local_functions()
792
        // .lift_all_local_functions();
793

794
        // debug!("About to expand defines");
795

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

798
        let mut analysis = semantic.into_analysis();
1✔
799

800
        let expanded_statements = flatten_begins_and_expand_defines(expanded_statements)?;
1✔
801

802
        let mut expanded_statements = filter_provides(expanded_statements);
803

804
        // After define expansion, we'll want this
805
        // RenameShadowedVariables::rename_shadowed_vars(&mut expanded_statements);
806

807
        self.shadowed_variable_renamer
808
            .rename_shadowed_variables(&mut expanded_statements, false);
809

810
        analysis.fresh_from_exprs(&expanded_statements);
811
        analysis.populate_captures(&expanded_statements);
812

813
        let mut semantic = SemanticAnalysis::from_analysis(&mut expanded_statements, analysis);
814

815
        semantic.check_if_values_are_redefined()?;
×
816

817
        semantic.refresh_variables();
1✔
818
        semantic.flatten_anonymous_functions();
1✔
819
        semantic.refresh_variables();
1✔
820

821
        // Replace mutation with boxes
822

823
        semantic.populate_captures_twice();
1✔
824

825
        semantic.replace_mutable_captured_variables_with_boxes();
1✔
826

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

829
        let mut analysis = semantic.into_analysis();
1✔
830

831
        // Rename them again
832
        // RenameShadowedVariables::rename_shadowed_vars(&mut expanded_statements);
833

834
        self.shadowed_variable_renamer
1✔
835
            .rename_shadowed_variables(&mut expanded_statements, false);
1✔
836

837
        // let mut expanded_statements =
838
        // log::info!(target: "expansion-phase", "Aggressive constant evaluation with memoization");
839

840
        // Begin lowering anonymous function calls to lets
841

842
        analysis.fresh_from_exprs(&expanded_statements);
1✔
843
        analysis.populate_captures(&expanded_statements);
1✔
844
        let mut semantic = SemanticAnalysis::from_analysis(&mut expanded_statements, analysis);
1✔
845

846
        semantic.replace_anonymous_function_calls_with_plain_lets();
1✔
847

848
        self.analysis = semantic.into_analysis();
1✔
849

850
        Ok(expanded_statements)
1✔
851
    }
852

853
    pub(crate) fn lower_expressions_impl(
802✔
854
        &mut self,
855
        exprs: Vec<ExprKind>,
856
        path: Option<PathBuf>,
857
    ) -> Result<Vec<ExprKind>> {
858
        #[cfg(feature = "profiling")]
859
        let now = Instant::now();
860

861
        // println!("Before expanding macros");
862
        // exprs.pretty_print();
863

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

866
        // println!("After expanding macros");
867
        // expanded_statements.pretty_print();
868

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

872
        #[cfg(feature = "profiling")]
873
        let now = Instant::now();
874

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

877
        if let Some(kernel) = self.kernel.as_mut() {
1,242✔
878
            // Label anything at the top as well - top level
879
            kernel.load_syntax_transformers(&mut expanded_statements, "top-level".to_string())?;
×
880
        }
881

882
        for expr in expanded_statements.iter_mut() {
22,525✔
883
            expand_kernel_in_env(
884
                expr,
21,736✔
885
                self.kernel.as_mut(),
21,736✔
886
                self.builtin_modules.clone(),
21,736✔
887
                "top-level",
888
            )?;
889
            crate::parser::expand_visitor::expand(expr, &self.macro_env)?;
21,735✔
890
            lower_entire_ast(expr)?;
21,744✔
891

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

896
                    crate::parser::expand_visitor::expand_with_source_id(
897
                        expr,
898
                        macro_env,
899
                        Some(source_id),
900
                    )?;
901
                }
902
            }
903

904
            // Lift all of the kernel macros as well?
905
            for (module, lifted_env) in &mut self.lifted_kernel_environments {
57,150✔
906
                let changed = expand_kernel_in_env_with_change(
907
                    expr,
908
                    self.kernel.as_mut(),
909
                    self.builtin_modules.clone(),
910
                    &module,
911
                )?;
912

913
                if changed {
×
914
                    lifted_env.name_mangler.visit(expr);
×
915
                }
916
            }
917

918
            expand_kernel_in_env(
919
                expr,
21,726✔
920
                self.kernel.as_mut(),
21,726✔
921
                self.builtin_modules.clone(),
21,726✔
922
                "top-level",
923
            )?;
924

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

928
            // for expr in expanded_statements.iter_mut() {
929
            lower_entire_ast(expr)?;
21,726✔
930
        }
931

932
        // TODO: Check that defines are in legal positions, post expansion.
933

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

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

939
        // steel_parser::ast::AstTools::pretty_print(&expanded_statements);
940

941
        // println!(
942
        //     "Modules: {:#?}",
943
        //     self.module_manager.modules().keys().collect::<Vec<_>>()
944
        // );
945

946
        // self.sources.debug_sources();
947

948
        let expanded_statements =
779✔
949
            self.apply_const_evaluation(constant_primitives(), expanded_statements, false)?;
779✔
950

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

953
        // TODO: Move this to its own function
954
        let mut expanded_statements = filter_provides(expanded_statements);
955

956
        #[cfg(feature = "profiling")]
957
        let now = Instant::now();
958

959
        // println!("--- Before renaming: ----");
960
        // expanded_statements.pretty_print();
961

962
        // TODO: Probably lift this above the const evaluation anyway
963
        self.shadowed_variable_renamer
964
            .rename_shadowed_variables(&mut expanded_statements, true);
965

966
        // println!("--- After renaming: ----");
967
        // expanded_statements.pretty_print();
968

969
        // let mut expanded_statements =
970
        //     self.apply_const_evaluation(constants.clone(), expanded_statements, false)?;
971

972
        let mut analysis = std::mem::take(&mut self.analysis);
973

974
        // Pre populate the analysis here
975
        analysis.fresh_from_exprs(&expanded_statements);
976

977
        // let mut analysis = Analysis::from_exprs(&expanded_statements);
978
        analysis.populate_captures(&expanded_statements);
979

980
        let mut semantic = SemanticAnalysis::from_analysis(&mut expanded_statements, analysis);
981

982
        // This is definitely broken still
983
        semantic
984
            .elide_single_argument_lambda_applications()
985
            .replace_non_shadowed_globals_with_builtins(
986
                &mut self.macro_env,
987
                &mut self.module_manager,
988
                &mut self.mangled_identifiers,
989
            )
990
            // TODO: To get this to work, we have to check the macros to make sure those
991
            // are safe to eliminate. In interactive mode, we'll
992
            // be unable to optimize those away
993
            .remove_unused_globals_with_prefix(
994
                MANGLER_PREFIX,
995
                &self.macro_env,
996
                &self.module_manager,
997
            )
998
            .lift_pure_local_functions()
999
            .lift_all_local_functions();
1000

1001
        // debug!("About to expand defines");
1002

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

1005
        let mut analysis = semantic.into_analysis();
779✔
1006

1007
        let mut expanded_statements = flatten_begins_and_expand_defines(expanded_statements)?;
779✔
1008

1009
        self.shadowed_variable_renamer
1010
            .rename_shadowed_variables(&mut expanded_statements, false);
1011

1012
        // After define expansion, we'll want this
1013
        // RenameShadowedVariables::rename_shadowed_vars(&mut expanded_statements);
1014

1015
        analysis.fresh_from_exprs(&expanded_statements);
1016
        analysis.populate_captures(&expanded_statements);
1017

1018
        // expanded_statements.pretty_print();
1019

1020
        let mut semantic = SemanticAnalysis::from_analysis(&mut expanded_statements, analysis);
1021

1022
        // Check if the values are redefined!
1023
        semantic.check_if_values_are_redefined()?;
×
1024

1025
        // semantic.refresh_variables();
1026
        semantic.flatten_anonymous_functions();
779✔
1027
        semantic.refresh_variables();
779✔
1028

1029
        // Replace mutation with boxes
1030
        semantic.populate_captures();
779✔
1031
        semantic.populate_captures();
779✔
1032

1033
        semantic.replace_mutable_captured_variables_with_boxes();
779✔
1034

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

1037
        let mut analysis = semantic.into_analysis();
779✔
1038

1039
        // Rename them again
1040
        // RenameShadowedVariables::rename_shadowed_vars(&mut expanded_statements);
1041
        self.shadowed_variable_renamer
779✔
1042
            .rename_shadowed_variables(&mut expanded_statements, false);
779✔
1043

1044
        // TODO - make sure I want to keep this
1045
        // let mut expanded_statements =
1046

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

1049
        // Begin lowering anonymous function calls to lets
1050
        analysis.fresh_from_exprs(&expanded_statements);
779✔
1051
        analysis.populate_captures(&expanded_statements);
779✔
1052
        let mut semantic = SemanticAnalysis::from_analysis(&mut expanded_statements, analysis);
779✔
1053
        // semantic.populate_captures();
1054

1055
        semantic.replace_anonymous_function_calls_with_plain_lets();
779✔
1056

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

1060
        self.analysis = semantic.into_analysis();
779✔
1061

1062
        // We don't want to leave this allocate memory just hanging around, but leave enough for
1063
        // interactive usages
1064
        self.analysis.shrink_capacity();
779✔
1065

1066
        // steel_parser::ast::AstTools::pretty_print(&expanded_statements);
1067

1068
        Ok(expanded_statements)
779✔
1069

1070
        // Done lowering anonymous function calls to let
1071
        // TODO: Re-enable this, but not in the repl. This repl causes... issues with the implementation
1072
        // self.apply_const_evaluation(constant_primitives(), expanded_statements, true)
1073
    }
1074

1075
    // TODO
1076
    // figure out how the symbols will work so that a raw program with symbols
1077
    // can be later pulled in and symbols can be interned correctly
1078
    fn compile_raw_program(
802✔
1079
        &mut self,
1080
        exprs: Vec<ExprKind>,
1081
        path: Option<PathBuf>,
1082
    ) -> Result<RawProgramWithSymbols> {
1083
        log::debug!(target: "expansion-phase", "Expanding macros -> phase 0");
802✔
1084

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

1087
        // println!("--- Final AST ---");
1088
        // println!("");
1089
        // steel_parser::ast::AstTools::pretty_print(&expanded_statements);
1090

1091
        // log::info!(
1092
        //     "{}",
1093
        //     expanded_statements
1094
        //         .iter()
1095
        //         .map(|x| x.to_pretty(60))
1096
        //         .join("\n\n")
1097
        // );
1098

1099
        log::debug!(target: "expansion-phase", "Generating instructions");
×
1100

1101
        let instructions = self.generate_instructions_for_executable(expanded_statements)?;
1,558✔
1102

1103
        let mut raw_program = RawProgramWithSymbols::new(
1104
            instructions,
779✔
1105
            self.constant_map.clone(),
779✔
1106
            "0.1.0".to_string(),
779✔
1107
        );
1108

1109
        // Make sure to apply the peephole optimizations
1110
        raw_program.apply_optimizations();
779✔
1111

1112
        // Lets see everything that gets run!
1113
        // raw_program.debug_print_log();
1114

1115
        Ok(raw_program)
779✔
1116
    }
1117

1118
    fn _run_const_evaluation_with_memoization(
×
1119
        &mut self,
1120
        mut _expanded_statements: Vec<ExprKind>,
1121
    ) -> Result<Vec<ExprKind>> {
1122
        todo!("Implement kernel level const evaluation here!")
1123
    }
1124

1125
    fn apply_const_evaluation(
779✔
1126
        &mut self,
1127
        constants: ImmutableHashMap<InternedString, SteelVal, FxBuildHasher>,
1128
        mut expanded_statements: Vec<ExprKind>,
1129
        use_kernel: bool,
1130
    ) -> Result<Vec<ExprKind>> {
1131
        #[cfg(feature = "profiling")]
1132
        let opt_time = Instant::now();
779✔
1133

1134
        let mut maybe_kernel = None;
779✔
1135

1136
        if use_kernel {
779✔
1137
            if let Some(kernel) = self.kernel.as_mut() {
×
1138
                kernel.load_program_for_comptime(constants.clone(), &mut expanded_statements)?;
×
1139
            }
1140
        }
1141

1142
        let mut manager = ConstantEvaluatorManager::new(
1143
            &mut self.memoization_table,
1144
            constants.clone(),
1145
            self.opt_level,
1146
            if use_kernel {
1147
                &mut self.kernel
×
1148
            } else {
1149
                &mut maybe_kernel
779✔
1150
            },
1151
        );
1152

1153
        match self.opt_level {
1154
            // TODO
1155
            // Cut this off at 10 iterations no matter what
1156
            OptLevel::Three => {
1157
                // for _ in 0..10 {
1158
                expanded_statements = manager.run(expanded_statements)?;
1,558✔
1159

1160
                // if !manager.changed {
1161
                //     break;
1162
                // }
1163

1164
                // manager.changed = false;
1165
                // }
1166
            }
1167
            OptLevel::Two => {
1168
                expanded_statements = ConstantEvaluatorManager::new(
×
1169
                    &mut self.memoization_table,
×
1170
                    constants,
×
1171
                    self.opt_level,
×
1172
                    if use_kernel {
×
1173
                        &mut self.kernel
×
1174
                    } else {
1175
                        &mut maybe_kernel
×
1176
                    },
1177
                )
1178
                .run(expanded_statements)?;
×
1179
            }
1180
            _ => {}
×
1181
        }
1182

1183
        #[cfg(feature = "profiling")]
1184
        if log::log_enabled!(target: "pipeline_time", log::Level::Debug) {
1185
            log::debug!(
1186
                target: "pipeline_time",
1187
                "Const Evaluation Time: {:?}",
1188
                opt_time.elapsed()
1189
            );
1190
        };
1191

1192
        Ok(expanded_statements)
779✔
1193
    }
1194
}
1195

1196
fn filter_provides(expanded_statements: Vec<ExprKind>) -> Vec<ExprKind> {
780✔
1197
    expanded_statements
780✔
1198
        .into_iter()
1199
        .filter_map(|expr| match expr {
22,507✔
1200
            ExprKind::Begin(mut b) => {
2,171✔
1201
                let exprs = std::mem::take(&mut b.exprs);
2,171✔
1202
                b.exprs = exprs
2,171✔
1203
                    .into_iter()
2,171✔
1204
                    .filter_map(|e| match e {
230,318✔
1205
                        ExprKind::List(l) => {
2,499✔
1206
                            if l.first_ident().copied() == Some(*PROVIDE) {
2,499✔
1207
                                return None;
4✔
1208
                            }
1209
                            return Some(ExprKind::List(l));
2,495✔
1210
                        }
1211
                        other => return Some(other),
225,648✔
1212
                    })
1213
                    .collect();
1214

1215
                Some(ExprKind::Begin(b))
1216
            }
1217
            ExprKind::List(l) => {
1,061✔
1218
                if l.first_ident().copied() == Some(*PROVIDE) {
1,061✔
1219
                    return None;
×
1220
                }
1221
                return Some(ExprKind::List(l));
1,061✔
1222
            }
1223
            other => Some(other),
18,495✔
1224
        })
1225
        .collect()
1226
}
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