rue_compiler/
file.rs

1use std::{collections::HashSet, sync::Arc};
2
3use clvmr::{Allocator, NodePtr};
4use id_arena::Arena;
5use indexmap::IndexMap;
6use rue_ast::{AstDocument, AstNode};
7use rue_diagnostic::{Diagnostic, DiagnosticSeverity, Source, SourceKind};
8use rue_hir::{
9    Declaration, DependencyGraph, Environment, Lowerer, ModuleDeclarations, Scope, ScopeId,
10    SymbolId,
11};
12use rue_lexer::Lexer;
13use rue_lir::{Error, codegen, optimize};
14use rue_options::CompilerOptions;
15use rue_parser::Parser;
16
17use crate::{
18    Compiler, check_unused, compile_symbol_items, compile_type_items, declare_symbol_items,
19    declare_type_items,
20};
21
22#[derive(Debug, Clone)]
23pub struct Compilation {
24    pub diagnostics: Vec<Diagnostic>,
25    pub main: Option<NodePtr>,
26    pub exports: IndexMap<String, NodePtr>,
27    pub tests: Vec<Test>,
28}
29
30#[derive(Debug, Clone)]
31pub struct Test {
32    pub name: String,
33    pub program: NodePtr,
34}
35
36#[derive(Debug, Clone)]
37struct PartialCompilation {
38    diagnostics: Vec<Diagnostic>,
39    scope: ScopeId,
40}
41
42pub fn compile_file(
43    allocator: &mut Allocator,
44    file: Source,
45    options: CompilerOptions,
46) -> Result<Compilation, Error> {
47    compile_file_impl(allocator, file, options, true)
48}
49
50pub fn analyze_file(file: Source, options: CompilerOptions) -> Result<Compilation, Error> {
51    compile_file_impl(&mut Allocator::new(), file, options, false)
52}
53
54fn compile_file_impl(
55    allocator: &mut Allocator,
56    file: Source,
57    options: CompilerOptions,
58    do_codegen: bool,
59) -> Result<Compilation, Error> {
60    let mut ctx = Compiler::new(options);
61    let std = compile_file_partial(
62        &mut ctx,
63        Source::new(Arc::from(include_str!("./std.rue")), SourceKind::Std),
64    );
65
66    let mut scope = Scope::new();
67
68    for (name, symbol) in ctx.scope(std.scope).exported_symbols() {
69        scope.insert_symbol(name.to_string(), symbol, false);
70    }
71
72    for (name, ty) in ctx.scope(std.scope).exported_types() {
73        scope.insert_type(name.to_string(), ty, false);
74    }
75
76    let scope = ctx.alloc_scope(scope);
77
78    ctx.push_scope(scope);
79    let file = compile_file_partial(&mut ctx, file);
80
81    let mut compilation = Compilation {
82        diagnostics: [std.diagnostics, file.diagnostics].concat(),
83        main: None,
84        exports: IndexMap::new(),
85        tests: Vec::new(),
86    };
87
88    let main_symbol = ctx.scope(file.scope).symbol("main");
89
90    let mut entrypoints = HashSet::new();
91
92    entrypoints.extend(main_symbol.map(Declaration::Symbol));
93
94    for test in ctx.tests() {
95        entrypoints.insert(Declaration::Symbol(test));
96    }
97
98    for (_, symbol) in ctx.scope(file.scope).exported_symbols() {
99        entrypoints.insert(Declaration::Symbol(symbol));
100    }
101
102    for (_, ty) in ctx.scope(file.scope).exported_types() {
103        entrypoints.insert(Declaration::Type(ty));
104    }
105
106    check_unused(&mut ctx, &entrypoints);
107
108    compilation.diagnostics.extend(ctx.take_diagnostics());
109
110    if !do_codegen
111        || compilation
112            .diagnostics
113            .iter()
114            .any(|d| d.kind.severity() == DiagnosticSeverity::Error)
115    {
116        return Ok(compilation);
117    }
118
119    if let Some(symbol) = main_symbol {
120        compilation.main = Some(generate(&mut ctx, allocator, symbol, options)?);
121    }
122
123    for (name, symbol) in ctx
124        .scope(file.scope)
125        .exported_symbols()
126        .map(|(name, symbol)| (name.to_string(), symbol))
127        .collect::<Vec<_>>()
128    {
129        compilation.exports.insert(
130            name.to_string(),
131            generate(&mut ctx, allocator, symbol, options)?,
132        );
133    }
134
135    for test in ctx.tests().collect::<Vec<_>>() {
136        let name = ctx.symbol(test).name().unwrap().text().to_string();
137
138        compilation.tests.push(Test {
139            name,
140            program: generate(&mut ctx, allocator, test, options)?,
141        });
142    }
143
144    Ok(compilation)
145}
146
147fn generate(
148    ctx: &mut Compiler,
149    allocator: &mut Allocator,
150    symbol: SymbolId,
151    options: CompilerOptions,
152) -> Result<NodePtr, Error> {
153    let graph = DependencyGraph::build(ctx, symbol, options);
154
155    let mut arena = Arena::new();
156    let mut lowerer = Lowerer::new(ctx, &mut arena, &graph, options, symbol);
157    let mut lir = lowerer.lower_symbol_value(&Environment::default(), symbol);
158
159    if options.optimize_lir {
160        lir = optimize(&mut arena, lir);
161    }
162
163    codegen(&arena, allocator, lir)
164}
165
166fn compile_file_partial(ctx: &mut Compiler, source: Source) -> PartialCompilation {
167    let tokens = Lexer::new(&source.text).collect::<Vec<_>>();
168    let parser = Parser::new(source.clone(), tokens);
169    let parse_result = parser.parse();
170
171    ctx.set_source(source);
172
173    let scope = ctx.alloc_scope(Scope::new());
174
175    let mut compilation = PartialCompilation {
176        diagnostics: parse_result.diagnostics,
177        scope,
178    };
179
180    let Some(ast) = AstDocument::cast(parse_result.node) else {
181        return compilation;
182    };
183
184    let mut declarations = ModuleDeclarations::default();
185
186    declare_type_items(ctx, scope, ast.items(), &mut declarations);
187    declare_symbol_items(ctx, scope, ast.items(), &mut declarations);
188    compile_type_items(ctx, scope, ast.items(), &declarations);
189    compile_symbol_items(ctx, scope, ast.items(), &declarations);
190
191    compilation.diagnostics.extend(ctx.take_diagnostics());
192
193    compilation
194}