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

mattwparas / steel / 11687386167

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

push

github

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

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

41 existing lines in 13 files now uncovered.

12433 of 26245 relevant lines covered (47.37%)

490139.96 hits per line

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

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

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

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

34
use crate::rvals::{Result, SteelVal};
35

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

42
// use crate::core::instructions::{densify, DenseInstruction};
43

44
use crate::stop;
45

46
use crate::steel_vm::const_evaluation::ConstantEvaluatorManager;
47

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

55
use crate::values::HashMap as ImmutableHashMap;
56

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

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

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

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

123
                    if let Some(x) = instructions.get_mut(i) {
70,834✔
124
                        x.payload_size = u24::from_usize(idx);
125
                    }
126
                }
127
                _ => {}
1,633,595✔
128
            }
129
        }
130

131
        Ok(())
23,856✔
132
    }
133

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

141
        let mut depth = 0;
23,852✔
142

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

212
                    let idx = symbol_map.get(s).map_err(|e| e.set_span(*span))?;
61,257✔
213

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

248
                    let idx = symbol_map.get(s).map_err(|e| e.set_span(*span))?;
271,548✔
249

250
                    // TODO commenting this for now
251
                    if let Some(x) = instructions.get_mut(i) {
271,538✔
252
                        x.payload_size = u24::from_usize(idx);
253
                    }
254
                }
255
                _ => {}
1,225,828✔
256
            }
257
        }
258

259
        Ok(())
23,831✔
260
    }
261
}
262

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

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

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

295
    analysis: Analysis,
296
    shadowed_variable_renamer: RenameShadowedVariables,
297

298
    search_dirs: Vec<PathBuf>,
299

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

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

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

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

329
        compiler
×
330
    }
331
}
332

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

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

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

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

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

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

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

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

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

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

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

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

476
        let expr_str = expr_str.into();
619✔
477

478
        let id = self.sources.add_source(expr_str.clone(), path.clone());
619✔
479

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

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

494
        // TODO fix this hack
495
        self.compile_raw_program(parsed?, path)
1,238✔
496
    }
497

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

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

514
        let parsed = parsed?;
×
515

516
        self.lower_expressions_impl(parsed, path)
517
    }
518

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

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

533
        let parsed = parsed?;
2✔
534

535
        self.expand_ast(parsed, path)
536
    }
537

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

UNCOV
553
    pub fn modules(&self) -> &FxHashMap<PathBuf, CompiledModule> {
×
UNCOV
554
        self.module_manager.modules()
×
555
    }
556

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

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

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

588
        let analysis = {
760✔
589
            let mut analysis = std::mem::take(&mut self.analysis);
760✔
590

591
            analysis.fresh_from_exprs(&expanded_statements);
760✔
592
            analysis.populate_captures_twice(&expanded_statements);
760✔
593

594
            // let mut analysis = Analysis::from_exprs(&expanded_statements);
595
            // analysis.populate_captures(&expanded_statements);
596
            // analysis.populate_captures(&expanded_statements);
597
            analysis
760✔
598
        };
599

600
        for expr in expanded_statements {
48,500✔
601
            let instructions =
23,870✔
602
                super::code_gen::CodeGenerator::new(&mut self.constant_map, &analysis)
603
                    .top_level_compile(&expr)?;
×
604

605
            results.push(instructions);
23,870✔
606
        }
607

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

614
        self.analysis = analysis;
760✔
615

616
        Ok(results)
760✔
617
    }
618

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

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

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

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

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

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

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

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

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

662
                    // crate::parser::expand_visitor::expand(expr, macro_env)?
663
                }
664
            }
665

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

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

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

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

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

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

696
        // let mut expanded_statements =
697
        // self.apply_const_evaluation(constants.clone(), expanded_statements, false)?;
698

699
        // expanded_statements.pretty_print();
700

701
        self.shadowed_variable_renamer
1✔
702
            .rename_shadowed_variables(&mut expanded_statements, true);
1✔
703

704
        let mut analysis = std::mem::take(&mut self.analysis);
1✔
705
        analysis.fresh_from_exprs(&expanded_statements);
1✔
706
        analysis.populate_captures(&expanded_statements);
1✔
707

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

710
        // This is definitely broken still
711
        semantic
1✔
712
            .elide_single_argument_lambda_applications()
713
            .replace_non_shadowed_globals_with_builtins(
714
                &mut self.macro_env,
1✔
715
                &mut self.module_manager,
1✔
716
                &mut self.mangled_identifiers,
1✔
717
            )
718
            // TODO: To get this to work, we have to check the macros to make sure those
719
            // are safe to eliminate. In interactive mode, we'll
720
            // be unable to optimize those away
721
            .remove_unused_globals_with_prefix("mangler", &self.macro_env, &self.module_manager);
1✔
722
        // Don't do lambda lifting here
723
        // .lift_pure_local_functions()
724
        // .lift_all_local_functions();
725

726
        // debug!("About to expand defines");
727

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

730
        let mut analysis = semantic.into_analysis();
1✔
731

732
        let mut expanded_statements = flatten_begins_and_expand_defines(expanded_statements)?;
1✔
733

734
        // After define expansion, we'll want this
735
        // RenameShadowedVariables::rename_shadowed_vars(&mut expanded_statements);
736

737
        self.shadowed_variable_renamer
738
            .rename_shadowed_variables(&mut expanded_statements, false);
739

740
        analysis.fresh_from_exprs(&expanded_statements);
741
        analysis.populate_captures(&expanded_statements);
742

743
        let mut semantic = SemanticAnalysis::from_analysis(&mut expanded_statements, analysis);
744

745
        semantic.check_if_values_are_redefined()?;
×
746

747
        semantic.refresh_variables();
1✔
748
        semantic.flatten_anonymous_functions();
1✔
749
        semantic.refresh_variables();
1✔
750

751
        // Replace mutation with boxes
752

753
        semantic.populate_captures_twice();
1✔
754

755
        semantic.replace_mutable_captured_variables_with_boxes();
1✔
756

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

759
        let mut analysis = semantic.into_analysis();
1✔
760

761
        // Rename them again
762
        // RenameShadowedVariables::rename_shadowed_vars(&mut expanded_statements);
763

764
        self.shadowed_variable_renamer
1✔
765
            .rename_shadowed_variables(&mut expanded_statements, false);
1✔
766

767
        // let mut expanded_statements =
768
        log::info!(target: "expansion-phase", "Aggressive constant evaluation with memoization");
1✔
769

770
        // Begin lowering anonymous function calls to lets
771

772
        analysis.fresh_from_exprs(&expanded_statements);
1✔
773
        analysis.populate_captures(&expanded_statements);
1✔
774
        let mut semantic = SemanticAnalysis::from_analysis(&mut expanded_statements, analysis);
1✔
775

776
        semantic.replace_anonymous_function_calls_with_plain_lets();
1✔
777

778
        self.analysis = semantic.into_analysis();
1✔
779

780
        Ok(expanded_statements)
1✔
781
    }
782

783
    pub(crate) fn lower_expressions_impl(
783✔
784
        &mut self,
785
        exprs: Vec<ExprKind>,
786
        path: Option<PathBuf>,
787
    ) -> Result<Vec<ExprKind>> {
788
        #[cfg(feature = "profiling")]
789
        let now = Instant::now();
790

791
        // println!("Before expanding macros");
792
        // exprs.pretty_print();
793

794
        let mut expanded_statements = self.expand_expressions(exprs, path)?;
1,566✔
795

796
        // println!("After expanding macros");
797
        // expanded_statements.pretty_print();
798

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

802
        #[cfg(feature = "profiling")]
803
        let now = Instant::now();
804

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

807
        if let Some(kernel) = self.kernel.as_mut() {
1,210✔
808
            // Label anything at the top as well - top level
809
            kernel.load_syntax_transformers(&mut expanded_statements, "top-level".to_string())?;
×
810
        }
811

812
        for expr in expanded_statements.iter_mut() {
22,149✔
813
            expand_kernel_in_env(
814
                expr,
21,379✔
815
                self.kernel.as_mut(),
21,379✔
816
                self.builtin_modules.clone(),
21,379✔
817
                "top-level",
818
            )?;
819
            crate::parser::expand_visitor::expand(expr, &self.macro_env)?;
21,378✔
820
            lower_entire_ast(expr)?;
21,387✔
821

822
            for module in &self.lifted_macro_environments {
201,989✔
823
                if let Some(macro_env) = self.modules().get(module).map(|x| &x.macro_map) {
180,620✔
824
                    let source_id = self.sources.get_source_id(module).unwrap();
825

826
                    crate::parser::expand_visitor::expand_with_source_id(
827
                        expr,
828
                        macro_env,
829
                        Some(source_id),
830
                    )?;
831
                }
832
            }
833

834
            // Lift all of the kernel macros as well?
835
            for (module, lifted_env) in &mut self.lifted_kernel_environments {
56,205✔
836
                let changed = expand_kernel_in_env_with_change(
837
                    expr,
838
                    self.kernel.as_mut(),
839
                    self.builtin_modules.clone(),
840
                    &module,
841
                )?;
842

843
                if changed {
×
844
                    lifted_env.name_mangler.visit(expr);
×
845
                }
846
            }
847

848
            expand_kernel_in_env(
849
                expr,
21,369✔
850
                self.kernel.as_mut(),
21,369✔
851
                self.builtin_modules.clone(),
21,369✔
852
                "top-level",
853
            )?;
854

855
            // TODO: If we have this, then we have to lower all of the expressions again
856
            crate::parser::expand_visitor::expand(expr, &self.macro_env)?;
21,369✔
857

858
            // for expr in expanded_statements.iter_mut() {
859
            lower_entire_ast(expr)?;
21,369✔
860
        }
861

862
        // TODO: Check that defines are in legal positions, post expansion.
863

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

867
        log::debug!(target: "expansion-phase", "Beginning constant folding");
760✔
868

869
        // steel_parser::ast::AstTools::pretty_print(&expanded_statements);
870

871
        // println!(
872
        //     "Modules: {:#?}",
873
        //     self.module_manager.modules().keys().collect::<Vec<_>>()
874
        // );
875

876
        // self.sources.debug_sources();
877

878
        let expanded_statements =
760✔
879
            self.apply_const_evaluation(constant_primitives(), expanded_statements, false)?;
760✔
880

881
        let mut expanded_statements = flatten_begins_and_expand_defines(expanded_statements)?;
760✔
882

883
        #[cfg(feature = "profiling")]
884
        let now = Instant::now();
885

886
        // println!("--- Before renaming: ----");
887
        // expanded_statements.pretty_print();
888

889
        // TODO: Probably lift this above the const evaluation anyway
890
        self.shadowed_variable_renamer
891
            .rename_shadowed_variables(&mut expanded_statements, true);
892

893
        // println!("--- After renaming: ----");
894
        // expanded_statements.pretty_print();
895

896
        // let mut expanded_statements =
897
        //     self.apply_const_evaluation(constants.clone(), expanded_statements, false)?;
898

899
        let mut analysis = std::mem::take(&mut self.analysis);
900

901
        // Pre populate the analysis here
902
        analysis.fresh_from_exprs(&expanded_statements);
903

904
        // let mut analysis = Analysis::from_exprs(&expanded_statements);
905
        analysis.populate_captures(&expanded_statements);
906

907
        let mut semantic = SemanticAnalysis::from_analysis(&mut expanded_statements, analysis);
908

909
        // This is definitely broken still
910
        semantic
911
            .elide_single_argument_lambda_applications()
912
            .replace_non_shadowed_globals_with_builtins(
913
                &mut self.macro_env,
914
                &mut self.module_manager,
915
                &mut self.mangled_identifiers,
916
            )
917
            // TODO: To get this to work, we have to check the macros to make sure those
918
            // are safe to eliminate. In interactive mode, we'll
919
            // be unable to optimize those away
920
            .remove_unused_globals_with_prefix("mangler", &self.macro_env, &self.module_manager)
921
            .lift_pure_local_functions()
922
            .lift_all_local_functions();
923

924
        // debug!("About to expand defines");
925

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

928
        let mut analysis = semantic.into_analysis();
760✔
929

930
        let mut expanded_statements = flatten_begins_and_expand_defines(expanded_statements)?;
760✔
931

932
        self.shadowed_variable_renamer
933
            .rename_shadowed_variables(&mut expanded_statements, false);
934

935
        // After define expansion, we'll want this
936
        // RenameShadowedVariables::rename_shadowed_vars(&mut expanded_statements);
937

938
        analysis.fresh_from_exprs(&expanded_statements);
939
        analysis.populate_captures(&expanded_statements);
940

941
        // expanded_statements.pretty_print();
942

943
        let mut semantic = SemanticAnalysis::from_analysis(&mut expanded_statements, analysis);
944

945
        // Check if the values are redefined!
946
        semantic.check_if_values_are_redefined()?;
×
947

948
        // semantic.refresh_variables();
949
        semantic.flatten_anonymous_functions();
760✔
950
        semantic.refresh_variables();
760✔
951

952
        // Replace mutation with boxes
953
        semantic.populate_captures();
760✔
954
        semantic.populate_captures();
760✔
955

956
        semantic.replace_mutable_captured_variables_with_boxes();
760✔
957

958
        log::debug!(target: "expansion-phase", "Expanding multiple arity functions");
760✔
959

960
        let mut analysis = semantic.into_analysis();
760✔
961

962
        // Rename them again
963
        // RenameShadowedVariables::rename_shadowed_vars(&mut expanded_statements);
964
        self.shadowed_variable_renamer
760✔
965
            .rename_shadowed_variables(&mut expanded_statements, false);
760✔
966

967
        // TODO - make sure I want to keep this
968
        // let mut expanded_statements =
969

970
        log::info!(target: "expansion-phase", "Aggressive constant evaluation with memoization");
760✔
971

972
        // Begin lowering anonymous function calls to lets
973
        analysis.fresh_from_exprs(&expanded_statements);
760✔
974
        analysis.populate_captures(&expanded_statements);
760✔
975
        let mut semantic = SemanticAnalysis::from_analysis(&mut expanded_statements, analysis);
760✔
976
        // semantic.populate_captures();
977

978
        semantic.replace_anonymous_function_calls_with_plain_lets();
760✔
979

980
        #[cfg(feature = "profiling")]
981
        log::info!(target: "pipeline_time", "CAT time: {:?}", now.elapsed());
760✔
982

983
        self.analysis = semantic.into_analysis();
760✔
984

985
        // We don't want to leave this allocate memory just hanging around, but leave enough for
986
        // interactive usages
987
        self.analysis.shrink_capacity();
760✔
988

989
        // steel_parser::ast::AstTools::pretty_print(&expanded_statements);
990

991
        Ok(expanded_statements)
760✔
992

993
        // Done lowering anonymous function calls to let
994
        // TODO: Re-enable this, but not in the repl. This repl causes... issues with the implementation
995
        // self.apply_const_evaluation(constant_primitives(), expanded_statements, true)
996
    }
997

998
    // TODO
999
    // figure out how the symbols will work so that a raw program with symbols
1000
    // can be later pulled in and symbols can be interned correctly
1001
    fn compile_raw_program(
783✔
1002
        &mut self,
1003
        exprs: Vec<ExprKind>,
1004
        path: Option<PathBuf>,
1005
    ) -> Result<RawProgramWithSymbols> {
1006
        log::debug!(target: "expansion-phase", "Expanding macros -> phase 0");
783✔
1007

1008
        let expanded_statements = self.lower_expressions_impl(exprs, path)?;
1,566✔
1009

1010
        // println!("--- Final AST ---");
1011
        // println!("");
1012
        // steel_parser::ast::AstTools::pretty_print(&expanded_statements);
1013

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

1016
        let instructions = self.generate_instructions_for_executable(expanded_statements)?;
1,520✔
1017

1018
        let mut raw_program = RawProgramWithSymbols::new(
1019
            instructions,
760✔
1020
            self.constant_map.clone(),
760✔
1021
            "0.1.0".to_string(),
760✔
1022
        );
1023

1024
        // Make sure to apply the peephole optimizations
1025
        raw_program.apply_optimizations();
760✔
1026

1027
        // Lets see everything that gets run!
1028
        // raw_program.debug_print();
1029

1030
        Ok(raw_program)
760✔
1031
    }
1032

1033
    fn _run_const_evaluation_with_memoization(
×
1034
        &mut self,
1035
        mut _expanded_statements: Vec<ExprKind>,
1036
    ) -> Result<Vec<ExprKind>> {
1037
        todo!("Implement kernel level const evaluation here!")
1038
    }
1039

1040
    fn apply_const_evaluation(
760✔
1041
        &mut self,
1042
        constants: ImmutableHashMap<InternedString, SteelVal, FxBuildHasher>,
1043
        mut expanded_statements: Vec<ExprKind>,
1044
        use_kernel: bool,
1045
    ) -> Result<Vec<ExprKind>> {
1046
        #[cfg(feature = "profiling")]
1047
        let opt_time = Instant::now();
760✔
1048

1049
        let mut maybe_kernel = None;
760✔
1050

1051
        if use_kernel {
760✔
1052
            if let Some(kernel) = self.kernel.as_mut() {
×
1053
                kernel.load_program_for_comptime(constants.clone(), &mut expanded_statements)?;
×
1054
            }
1055
        }
1056

1057
        let mut manager = ConstantEvaluatorManager::new(
1058
            &mut self.memoization_table,
1059
            constants.clone(),
1060
            self.opt_level,
1061
            if use_kernel {
1062
                &mut self.kernel
×
1063
            } else {
1064
                &mut maybe_kernel
760✔
1065
            },
1066
        );
1067

1068
        match self.opt_level {
1069
            // TODO
1070
            // Cut this off at 10 iterations no matter what
1071
            OptLevel::Three => {
1072
                // for _ in 0..10 {
1073
                expanded_statements = manager.run(expanded_statements)?;
1,520✔
1074

1075
                // if !manager.changed {
1076
                //     break;
1077
                // }
1078

1079
                // manager.changed = false;
1080
                // }
1081
            }
1082
            OptLevel::Two => {
1083
                expanded_statements = ConstantEvaluatorManager::new(
×
1084
                    &mut self.memoization_table,
×
1085
                    constants,
×
1086
                    self.opt_level,
×
1087
                    if use_kernel {
×
1088
                        &mut self.kernel
×
1089
                    } else {
1090
                        &mut maybe_kernel
×
1091
                    },
1092
                )
1093
                .run(expanded_statements)?;
×
1094
            }
1095
            _ => {}
×
1096
        }
1097

1098
        #[cfg(feature = "profiling")]
1099
        if log::log_enabled!(target: "pipeline_time", log::Level::Debug) {
1100
            log::debug!(
1101
                target: "pipeline_time",
1102
                "Const Evaluation Time: {:?}",
1103
                opt_time.elapsed()
1104
            );
1105
        };
1106

1107
        Ok(expanded_statements)
760✔
1108
    }
1109
}
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