rue_compiler/
file.rs

1use std::{
2    collections::{HashMap, HashSet},
3    fs, io,
4    path::Path,
5    sync::Arc,
6};
7
8use clvmr::{Allocator, NodePtr};
9use id_arena::Arena;
10use indexmap::{IndexMap, IndexSet, indexset};
11use rowan::{TextRange, TextSize};
12use rue_ast::{AstDocument, AstNode};
13use rue_diagnostic::{Name, Source, SourceKind};
14use rue_hir::{
15    Declaration, DependencyGraph, Environment, Lowerer, ModuleDeclarations, ModuleSymbol, Scope,
16    ScopeId, Symbol, SymbolId,
17};
18use rue_lexer::Lexer;
19use rue_parser::Parser;
20use thiserror::Error;
21
22use crate::{
23    Compiler, ImportCache, SyntaxItemKind, check_unused, compile_symbol_items, compile_type_items,
24    declare_module_items, declare_symbol_items, declare_type_items, resolve_imports,
25};
26
27#[derive(Debug, Error)]
28pub enum Error {
29    #[error("IO error: {0}")]
30    Io(#[from] io::Error),
31
32    #[error("Codegen error: {0}")]
33    Lir(#[from] rue_lir::Error),
34
35    #[error("Source not found in compilation unit: {0}")]
36    SourceNotFound(SourceKind),
37}
38
39#[derive(Debug, Clone)]
40pub struct CompiledTest {
41    pub name: Option<String>,
42    pub path: SourceKind,
43    pub symbol: SymbolId,
44    pub ptr: NodePtr,
45}
46
47#[derive(Debug, Clone)]
48pub struct CompiledExport {
49    pub name: String,
50    pub symbol: SymbolId,
51    pub ptr: NodePtr,
52}
53
54#[derive(Debug, Clone)]
55pub enum FileTree {
56    File(File),
57    Directory(Directory),
58}
59
60impl FileTree {
61    pub fn compile_file(ctx: &mut Compiler, path: &Path, file_name: String) -> Result<Self, Error> {
62        let tree = Self::File(File::new(
63            ctx,
64            "main".to_string(),
65            Source::new(
66                Arc::from(fs::read_to_string(path)?),
67                SourceKind::File(file_name),
68            ),
69        ));
70
71        tree.compile(ctx);
72
73        Ok(tree)
74    }
75
76    pub fn compile_path(
77        ctx: &mut Compiler,
78        path: &Path,
79        cache: &mut HashMap<SourceKind, String>,
80    ) -> Result<Self, Error> {
81        let tree = Self::try_from_path(ctx, path, cache)?
82            .ok_or(io::Error::other("Invalid file extension"))?;
83
84        tree.compile(ctx);
85
86        Ok(tree)
87    }
88
89    fn try_from_path(
90        ctx: &mut Compiler,
91        path: &Path,
92        cache: &mut HashMap<SourceKind, String>,
93    ) -> Result<Option<Self>, Error> {
94        let file_name = path
95            .file_name()
96            .ok_or(io::Error::new(
97                io::ErrorKind::InvalidFilename,
98                "Missing file name",
99            ))?
100            .to_string_lossy()
101            .to_string();
102
103        if fs::metadata(path)?.is_file() {
104            #[allow(clippy::case_sensitive_file_extension_comparisons)]
105            if !file_name.ends_with(".rue") {
106                return Ok(None);
107            }
108
109            let source_kind = normalize_path(path)?;
110
111            let text = if let Some(text) = cache.get(&source_kind) {
112                text.clone()
113            } else {
114                fs::read_to_string(path)?
115            };
116
117            let source = Source::new(Arc::from(text), source_kind);
118
119            Ok(Some(Self::File(File::new(
120                ctx,
121                file_name.replace(".rue", ""),
122                source,
123            ))))
124        } else {
125            let scope = ctx.alloc_child_scope();
126
127            let module = ctx.alloc_symbol(Symbol::Module(ModuleSymbol {
128                name: Some(Name::new(file_name.as_str(), None)),
129                scope,
130                declarations: ModuleDeclarations::default(),
131            }));
132
133            let mut children = Vec::new();
134
135            for entry in fs::read_dir(path)? {
136                let entry = entry?;
137                let path = entry.path();
138                let tree = Self::try_from_path(ctx, &path, cache)?;
139                children.extend(tree);
140            }
141
142            Ok(Some(Self::Directory(Directory::new(
143                ctx, file_name, module, children,
144            ))))
145        }
146    }
147
148    pub fn name(&self) -> &str {
149        match self {
150            Self::File(file) => &file.name,
151            Self::Directory(directory) => &directory.name,
152        }
153    }
154
155    pub fn module(&self) -> SymbolId {
156        match self {
157            Self::File(file) => file.module,
158            Self::Directory(directory) => directory.module,
159        }
160    }
161
162    pub fn all_modules(&self) -> IndexSet<SymbolId> {
163        match self {
164            Self::File(file) => indexset![file.module],
165            Self::Directory(directory) => directory
166                .children
167                .iter()
168                .flat_map(Self::all_modules)
169                .collect(),
170        }
171    }
172
173    pub fn all_files(&self) -> Vec<&File> {
174        match self {
175            Self::File(file) => vec![file],
176            Self::Directory(directory) => directory
177                .children
178                .iter()
179                .flat_map(Self::all_files)
180                .collect(),
181        }
182    }
183
184    pub fn compile(&self, ctx: &mut Compiler) {
185        self.compile_impl(ctx, true);
186    }
187
188    pub(crate) fn compile_impl(&self, ctx: &mut Compiler, unused_check: bool) {
189        let mut cache = ImportCache::default();
190
191        let modules = self.all_modules().into_iter().collect::<Vec<_>>();
192
193        self.declare_modules(ctx);
194        self.declare_types(ctx);
195
196        resolve_imports(ctx, modules.clone(), &mut cache, false);
197
198        self.declare_symbols(ctx);
199
200        resolve_imports(ctx, modules.clone(), &mut cache, false);
201        resolve_imports(ctx, modules.clone(), &mut cache, true);
202
203        self.compile_types(ctx);
204        self.compile_symbols(ctx);
205
206        if unused_check {
207            let entrypoints = self.entrypoints(ctx);
208            check_unused(ctx, &entrypoints);
209        }
210    }
211
212    pub fn entrypoints(&self, ctx: &Compiler) -> HashSet<Declaration> {
213        let mut entrypoints = ctx
214            .tests()
215            .map(|test| Declaration::Symbol(test.symbol))
216            .collect::<HashSet<_>>();
217
218        let mut stack = vec![self];
219
220        while let Some(current) = stack.pop() {
221            match current {
222                Self::File(file) => {
223                    let scope = file.module(ctx).scope;
224
225                    if let Some(main) = ctx.scope(scope).symbol("main") {
226                        entrypoints.insert(Declaration::Symbol(main));
227                    }
228
229                    ctx.scope(scope).exported_symbols().for_each(|(_, symbol)| {
230                        entrypoints.insert(Declaration::Symbol(symbol));
231                    });
232
233                    ctx.scope(scope).exported_types().for_each(|(_, ty)| {
234                        entrypoints.insert(Declaration::Type(ty));
235                    });
236                }
237                Self::Directory(directory) => {
238                    directory.children.iter().for_each(|child| {
239                        stack.push(child);
240                    });
241                }
242            }
243        }
244
245        entrypoints
246    }
247
248    pub fn main(
249        &self,
250        ctx: &mut Compiler,
251        allocator: &mut Allocator,
252        path: &SourceKind,
253    ) -> Result<Option<NodePtr>, Error> {
254        let tree = self
255            .find(path)
256            .ok_or_else(|| Error::SourceNotFound(path.clone()))?;
257
258        let scope = ctx.module(tree.module()).scope;
259        let Some(main) = ctx.scope(scope).symbol("main") else {
260            return Ok(None);
261        };
262
263        Ok(Some(codegen(ctx, allocator, main)?))
264    }
265
266    pub fn exports(
267        &self,
268        ctx: &mut Compiler,
269        allocator: &mut Allocator,
270        path: Option<&SourceKind>,
271        export_name: Option<&str>,
272    ) -> Result<Vec<CompiledExport>, Error> {
273        let Some(path) = path else {
274            return Ok(self
275                .all_files()
276                .iter()
277                .map(|file| self.exports(ctx, allocator, Some(&file.source.kind), export_name))
278                .collect::<Result<Vec<_>, Error>>()?
279                .into_iter()
280                .flatten()
281                .collect());
282        };
283
284        let tree = self
285            .find(path)
286            .ok_or_else(|| Error::SourceNotFound(path.clone()))?;
287
288        let scope = ctx.module(tree.module()).scope;
289
290        let mut exports = Vec::new();
291
292        for (name, symbol) in ctx
293            .scope(scope)
294            .exported_symbols()
295            .map(|(name, symbol)| (name.to_string(), symbol))
296            .collect::<Vec<_>>()
297        {
298            if let Some(export_name) = export_name
299                && name != export_name
300            {
301                continue;
302            }
303
304            let ptr = codegen(ctx, allocator, symbol)?;
305            exports.push(CompiledExport { name, symbol, ptr });
306        }
307
308        Ok(exports)
309    }
310
311    pub fn tests(
312        &self,
313        ctx: &mut Compiler,
314        allocator: &mut Allocator,
315        path: Option<&SourceKind>,
316        search_term: Option<&str>,
317    ) -> Result<Vec<CompiledTest>, Error> {
318        let tests = ctx
319            .tests()
320            .filter(|test| {
321                if let Some(path) = path
322                    && &test.path != path
323                {
324                    return false;
325                }
326
327                if let Some(search_term) = search_term {
328                    let Some(name) = &test.name else {
329                        return false;
330                    };
331
332                    return name.to_lowercase().contains(&search_term.to_lowercase());
333                }
334
335                true
336            })
337            .cloned()
338            .collect::<Vec<_>>();
339
340        let mut outputs = Vec::new();
341
342        for test in tests {
343            let ptr = codegen(ctx, allocator, test.symbol)?;
344            outputs.push(CompiledTest {
345                name: test.name,
346                path: test.path,
347                symbol: test.symbol,
348                ptr,
349            });
350        }
351
352        Ok(outputs)
353    }
354
355    pub fn find(&self, path: &SourceKind) -> Option<&Self> {
356        match self {
357            Self::File(file) => {
358                if file.source.kind == *path {
359                    Some(self)
360                } else {
361                    None
362                }
363            }
364            Self::Directory(directory) => {
365                directory.children.iter().find_map(|child| child.find(path))
366            }
367        }
368    }
369
370    fn declare_modules(&self, ctx: &mut Compiler) {
371        match self {
372            Self::File(file) => file.declare_modules(ctx),
373            Self::Directory(directory) => directory.declare_modules(ctx),
374        }
375    }
376
377    fn declare_types(&self, ctx: &mut Compiler) {
378        match self {
379            Self::File(file) => file.declare_types(ctx),
380            Self::Directory(directory) => directory.declare_types(ctx),
381        }
382    }
383
384    fn declare_symbols(&self, ctx: &mut Compiler) {
385        match self {
386            Self::File(file) => file.declare_symbols(ctx),
387            Self::Directory(directory) => directory.declare_symbols(ctx),
388        }
389    }
390
391    fn compile_types(&self, ctx: &mut Compiler) {
392        match self {
393            Self::File(file) => file.compile_types(ctx),
394            Self::Directory(directory) => directory.compile_types(ctx),
395        }
396    }
397
398    fn compile_symbols(&self, ctx: &mut Compiler) {
399        match self {
400            Self::File(file) => file.compile_symbols(ctx),
401            Self::Directory(directory) => directory.compile_symbols(ctx),
402        }
403    }
404}
405
406#[derive(Debug, Clone)]
407pub struct File {
408    pub name: String,
409    pub source: Source,
410    pub document: AstDocument,
411    pub module: SymbolId,
412    pub sibling_scope: ScopeId,
413}
414
415impl File {
416    pub fn new(ctx: &mut Compiler, name: String, source: Source) -> Self {
417        let tokens = Lexer::new(&source.text).collect::<Vec<_>>();
418        let parser = Parser::new(source.clone(), tokens);
419        let parse_result = parser.parse();
420
421        ctx.extend_diagnostics(parse_result.diagnostics);
422
423        let document = AstDocument::cast(parse_result.node).unwrap();
424
425        let sibling_scope = ctx.alloc_child_scope();
426        let scope = ctx.alloc_scope(Scope::new(Some(sibling_scope)));
427
428        let module = ctx.alloc_symbol(Symbol::Module(ModuleSymbol {
429            name: Some(Name::new(name.as_str(), None)),
430            scope,
431            declarations: ModuleDeclarations::default(),
432        }));
433
434        ctx.add_syntax_for_source(
435            SyntaxItemKind::FileModule(module),
436            document.syntax().text_range(),
437            source.kind.clone(),
438        );
439
440        Self {
441            name,
442            source,
443            document,
444            module,
445            sibling_scope,
446        }
447    }
448
449    pub fn std(ctx: &mut Compiler) -> Self {
450        let text = include_str!("./std.rue");
451
452        Self::new(
453            ctx,
454            "std".to_string(),
455            Source::new(Arc::from(text), SourceKind::Std),
456        )
457    }
458
459    pub(crate) fn module<'a>(&'a self, ctx: &'a Compiler) -> &'a ModuleSymbol {
460        match ctx.symbol(self.module) {
461            Symbol::Module(module) => module,
462            _ => unreachable!(),
463        }
464    }
465
466    fn module_mut<'a>(&'a self, ctx: &'a mut Compiler) -> &'a mut ModuleSymbol {
467        match ctx.symbol_mut(self.module) {
468            Symbol::Module(module) => module,
469            _ => unreachable!(),
470        }
471    }
472
473    fn begin(&self, ctx: &mut Compiler) -> ModuleDeclarations {
474        let scope = self.module(ctx).scope;
475        ctx.set_source(self.source.clone());
476        ctx.push_scope(
477            self.sibling_scope,
478            self.document.syntax().text_range().start(),
479        );
480        ctx.push_scope(scope, self.document.syntax().text_range().start());
481        // ctx.push_module(self.module);
482        self.module(ctx).declarations.clone()
483    }
484
485    fn end(&self, ctx: &mut Compiler, declarations: ModuleDeclarations) {
486        // ctx.pop_module();
487        ctx.pop_scope(self.document.syntax().text_range().end());
488        ctx.pop_scope(self.document.syntax().text_range().end());
489        self.module_mut(ctx).declarations = declarations;
490    }
491
492    fn declare_modules(&self, ctx: &mut Compiler) {
493        let mut declarations = self.begin(ctx);
494        declare_module_items(ctx, self.document.items(), &mut declarations);
495        self.end(ctx, declarations);
496    }
497
498    fn declare_types(&self, ctx: &mut Compiler) {
499        let mut declarations = self.begin(ctx);
500        declare_type_items(ctx, self.document.items(), &mut declarations);
501        self.end(ctx, declarations);
502    }
503
504    fn declare_symbols(&self, ctx: &mut Compiler) {
505        let mut declarations = self.begin(ctx);
506        declare_symbol_items(ctx, self.document.items(), &mut declarations);
507        self.end(ctx, declarations);
508    }
509
510    fn compile_types(&self, ctx: &mut Compiler) {
511        let declarations = self.begin(ctx);
512        compile_type_items(ctx, self.document.items(), &declarations);
513        self.end(ctx, declarations);
514    }
515
516    fn compile_symbols(&self, ctx: &mut Compiler) {
517        let declarations = self.begin(ctx);
518        compile_symbol_items(ctx, self.document.items(), &declarations);
519        self.end(ctx, declarations);
520
521        for scope in ctx.scope_stack().into_iter().rev() {
522            ctx.add_syntax(
523                SyntaxItemKind::Scope(scope),
524                TextRange::new(
525                    TextSize::from(0),
526                    TextSize::from(self.source.text.len() as u32),
527                ),
528            );
529        }
530    }
531}
532
533#[derive(Debug, Clone)]
534pub struct Directory {
535    pub name: String,
536    pub children: Vec<FileTree>,
537    pub module: SymbolId,
538}
539
540impl Directory {
541    pub fn new(
542        ctx: &mut Compiler,
543        name: String,
544        module: SymbolId,
545        children: Vec<FileTree>,
546    ) -> Self {
547        let scope = ctx.module(module).scope;
548
549        let mut child_modules = IndexMap::new();
550
551        for child in &children {
552            let name = child.name().to_string();
553            let module = child.module();
554            child_modules.insert(name.clone(), module);
555            ctx.scope_mut(scope).insert_symbol(name, module, true);
556        }
557
558        for child in &children {
559            let mut child_modules = child_modules.clone();
560
561            child_modules.shift_remove(child.name());
562
563            for (name, module) in child_modules {
564                if let FileTree::File(file) = child {
565                    ctx.scope_mut(file.sibling_scope)
566                        .insert_symbol(name, module, false);
567                }
568            }
569        }
570
571        Self {
572            name,
573            children,
574            module,
575        }
576    }
577
578    fn begin(&self, ctx: &mut Compiler) {
579        ctx.push_module(self.module);
580    }
581
582    #[allow(clippy::unused_self)]
583    fn end(&self, ctx: &mut Compiler) {
584        ctx.pop_module();
585    }
586
587    fn declare_modules(&self, ctx: &mut Compiler) {
588        self.begin(ctx);
589        for child in &self.children {
590            child.declare_modules(ctx);
591        }
592        self.end(ctx);
593    }
594
595    fn declare_types(&self, ctx: &mut Compiler) {
596        self.begin(ctx);
597        for child in &self.children {
598            child.declare_types(ctx);
599        }
600        self.end(ctx);
601    }
602
603    fn declare_symbols(&self, ctx: &mut Compiler) {
604        self.begin(ctx);
605        for child in &self.children {
606            child.declare_symbols(ctx);
607        }
608        self.end(ctx);
609    }
610
611    fn compile_types(&self, ctx: &mut Compiler) {
612        self.begin(ctx);
613        for child in &self.children {
614            child.compile_types(ctx);
615        }
616        self.end(ctx);
617    }
618
619    fn compile_symbols(&self, ctx: &mut Compiler) {
620        self.begin(ctx);
621        for child in &self.children {
622            child.compile_symbols(ctx);
623        }
624        self.end(ctx);
625    }
626}
627
628pub fn normalize_path(path: &Path) -> Result<SourceKind, Error> {
629    Ok(SourceKind::File(
630        path.canonicalize()?.to_string_lossy().to_string(),
631    ))
632}
633
634fn codegen(
635    ctx: &mut Compiler,
636    allocator: &mut Allocator,
637    symbol: SymbolId,
638) -> Result<NodePtr, Error> {
639    let options = *ctx.options();
640    let graph = DependencyGraph::build(ctx, symbol, options);
641
642    let mut arena = Arena::new();
643    let mut lowerer = Lowerer::new(ctx, &mut arena, &graph, options, symbol);
644    let mut lir = lowerer.lower_symbol_value(&Environment::default(), symbol);
645
646    if options.optimize_lir {
647        lir = rue_lir::optimize(&mut arena, lir);
648    }
649
650    Ok(rue_lir::codegen(&arena, allocator, lir)?)
651}