diff --git a/crates/rue-cli/src/main.rs b/crates/rue-cli/src/main.rs index 18d0c9d..c303bc0 100644 --- a/crates/rue-cli/src/main.rs +++ b/crates/rue-cli/src/main.rs @@ -2,7 +2,7 @@ use std::fs; use clap::Parser; use clvmr::{run_program, serde::node_to_bytes, Allocator, ChiaDialect, NodePtr}; -use rue_compiler::{compile, DiagnosticKind}; +use rue_compiler::{analyze, compile, Diagnostic, DiagnosticKind}; use rue_parser::{line_col, parse, LineCol}; /// The Rue language compiler and toolchain. @@ -11,6 +11,10 @@ use rue_parser::{line_col, parse, LineCol}; struct Args { /// The source file to compile. file: String, + + /// Whether to only analyze. + #[clap(long, short)] + analyze: bool, } fn main() { @@ -27,46 +31,53 @@ fn main() { eprintln!("{} at {line}:{col}", error.kind()); } - let mut allocator = Allocator::new(); - let output = compile(&mut allocator, &ast, errors.is_empty()); - - if !output.diagnostics().is_empty() { - let mut has_error = false; + if args.analyze { + let diagnostics = analyze(&ast); + print_diagnostics(&source, &diagnostics); + } else { + let mut allocator = Allocator::new(); + let output = compile(&mut allocator, &ast, errors.is_empty()); - for error in output.diagnostics() { - let LineCol { line, col } = line_col(&source, error.span().start); - let line = line + 1; - let col = col + 1; - - match error.kind() { - DiagnosticKind::Error(kind) => { - has_error = true; - eprintln!("Error: {kind} at {line}:{col}"); - } - DiagnosticKind::Warning(kind) => { - eprintln!("Warning: {kind} at {line}:{col}"); - } - } + if print_diagnostics(&source, output.diagnostics()) { + return; } - if has_error { - return; + let bytes = node_to_bytes(&allocator, output.node_ptr()).unwrap(); + println!("{}", hex::encode(bytes)); + match run_program( + &mut allocator, + &ChiaDialect::new(0), + output.node_ptr(), + NodePtr::NIL, + 0, + ) { + Ok(output) => eprintln!( + "Serialized output: {}", + hex::encode(node_to_bytes(&allocator, output.1).unwrap()) + ), + Err(error) => eprintln!("Error: {error:?}"), } } +} + +fn print_diagnostics(source: &str, diagnostics: &[Diagnostic]) -> bool { + let mut has_error = false; - let bytes = node_to_bytes(&allocator, output.node_ptr()).unwrap(); - println!("{}", hex::encode(bytes)); - match run_program( - &mut allocator, - &ChiaDialect::new(0), - output.node_ptr(), - NodePtr::NIL, - 0, - ) { - Ok(output) => eprintln!( - "Serialized output: {}", - hex::encode(node_to_bytes(&allocator, output.1).unwrap()) - ), - Err(error) => eprintln!("Error: {error:?}"), + for error in diagnostics { + let LineCol { line, col } = line_col(source, error.span().start); + let line = line + 1; + let col = col + 1; + + match error.kind() { + DiagnosticKind::Error(kind) => { + has_error = true; + eprintln!("Error: {kind} at {line}:{col}"); + } + DiagnosticKind::Warning(kind) => { + eprintln!("Warning: {kind} at {line}:{col}"); + } + } } + + has_error } diff --git a/crates/rue-compiler/src/compiler/context.rs b/crates/rue-compiler/src/compiler/context.rs index cbe9589..2bb1803 100644 --- a/crates/rue-compiler/src/compiler/context.rs +++ b/crates/rue-compiler/src/compiler/context.rs @@ -1,3 +1,5 @@ +use std::collections::HashSet; + use clvmr::{Allocator, NodePtr}; use indexmap::IndexMap; use rue_parser::{parse, Root}; @@ -5,6 +7,7 @@ use rue_parser::{parse, Root}; use crate::{ codegen::Codegen, optimizer::{DependencyGraph, Optimizer}, + scope::Scope, symbol::{Module, Symbol}, Database, SymbolId, }; @@ -25,14 +28,46 @@ pub fn setup_compiler(db: &mut Database) -> CompilerContext<'_> { } } -pub fn load_standard_library(ctx: &mut CompilerContext<'_>) { +pub fn load_standard_library(ctx: &mut CompilerContext<'_>) -> SymbolId { let (root, parser_errors) = parse(include_str!("../../../../std/stdlib.rue")); assert_eq!(parser_errors, Vec::new()); - let module_id = load_module(ctx, &root); + + let (module_id, declarations) = ctx.compiler.declare_root(&root); + ctx.compiler.compile_root(&root, module_id, declarations); + let Symbol::Module(module) = ctx.compiler.db.symbol_mut(module_id).clone() else { unreachable!(); }; - ctx.compiler.scope_stack.push(module.scope_id); + + let mut scope = Scope::default(); + + for &symbol_id in &module.exported_symbols { + scope.define_symbol( + ctx.compiler + .db + .scope(module.scope_id) + .symbol_name(symbol_id) + .unwrap() + .to_string(), + symbol_id, + ); + } + + for &type_id in &module.exported_types { + scope.define_type( + ctx.compiler + .db + .scope(module.scope_id) + .type_name(type_id) + .unwrap() + .to_string(), + type_id, + ); + } + + let scope_id = ctx.compiler.db.alloc_scope(scope); + ctx.compiler.scope_stack.push(scope_id); + module_id } pub fn load_module(ctx: &mut CompilerContext<'_>, root: &Root) -> SymbolId { @@ -51,13 +86,26 @@ pub fn compile_modules(mut ctx: CompilerContext<'_>) -> SymbolTable { pub fn build_graph( db: &mut Database, symbol_table: &SymbolTable, - entrypoint: SymbolId, + main_module_id: SymbolId, + library_module_ids: &[SymbolId], ) -> DependencyGraph { - let Symbol::Module(module) = db.symbol_mut(entrypoint).clone() else { + let mut ignored_symbols = HashSet::new(); + let mut ignored_types = HashSet::new(); + + for &module_id in library_module_ids { + let Symbol::Module(module) = db.symbol_mut(module_id).clone() else { + unreachable!(); + }; + ignored_symbols.extend(module.exported_symbols.iter().copied()); + ignored_types.extend(module.exported_types.iter().copied()); + } + + let Symbol::Module(module) = db.symbol_mut(main_module_id).clone() else { unreachable!(); }; + let graph = DependencyGraph::build(db, &module); - symbol_table.calculate_unused(db, &graph, &module); + symbol_table.calculate_unused(db, &graph, &ignored_symbols, &ignored_types); graph } diff --git a/crates/rue-compiler/src/compiler/expr/function_call_expr.rs b/crates/rue-compiler/src/compiler/expr/function_call_expr.rs index 8cf8406..8ec8ad2 100644 --- a/crates/rue-compiler/src/compiler/expr/function_call_expr.rs +++ b/crates/rue-compiler/src/compiler/expr/function_call_expr.rs @@ -18,6 +18,8 @@ impl Compiler<'_> { let expected = if let Type::Function(fun) = self.db.ty(callee.type_id) { Some(fun.clone()) + } else if let Type::Unknown = self.db.ty(callee.type_id) { + None } else { self.db.error( ErrorKind::UncallableType(self.type_name(callee.type_id)), diff --git a/crates/rue-compiler/src/compiler/item/module_item.rs b/crates/rue-compiler/src/compiler/item/module_item.rs index f94bc32..2adffbd 100644 --- a/crates/rue-compiler/src/compiler/item/module_item.rs +++ b/crates/rue-compiler/src/compiler/item/module_item.rs @@ -28,10 +28,17 @@ impl Compiler<'_> { /// Compile the root by lowering all items into scope. pub fn compile_root(&mut self, root: &Root, module_id: SymbolId, declarations: Declarations) { - let Symbol::Module(Module { scope_id, .. }) = self.db.symbol_mut(module_id).clone() else { + let Symbol::Module(Module { + scope_id, + exported_symbols, + exported_types, + }) = self.db.symbol_mut(module_id) + else { unreachable!(); }; - self.scope_stack.push(scope_id); + exported_symbols.extend(declarations.exported_symbols.clone()); + exported_types.extend(declarations.exported_types.clone()); + self.scope_stack.push(*scope_id); self.compile_items(&root.items(), declarations); self.scope_stack.pop().unwrap(); } diff --git a/crates/rue-compiler/src/compiler/symbol_table.rs b/crates/rue-compiler/src/compiler/symbol_table.rs index 423d6e1..4798e51 100644 --- a/crates/rue-compiler/src/compiler/symbol_table.rs +++ b/crates/rue-compiler/src/compiler/symbol_table.rs @@ -3,10 +3,7 @@ use std::collections::HashSet; use indexmap::{IndexMap, IndexSet}; use crate::{ - optimizer::DependencyGraph, - symbol::{Module, Symbol}, - ty::Type, - Database, SymbolId, TypeId, WarningKind, + optimizer::DependencyGraph, symbol::Symbol, ty::Type, Database, SymbolId, TypeId, WarningKind, }; #[derive(Debug, Default)] @@ -47,7 +44,8 @@ impl SymbolTable { &self, db: &mut Database, dependency_graph: &DependencyGraph, - module: &Module, + ignored_symbols: &HashSet, + ignored_types: &HashSet, ) { let mut type_ids = IndexSet::new(); let mut symbol_ids = IndexSet::new(); @@ -62,8 +60,7 @@ impl SymbolTable { .flat_map(|scope_id| db.scope(scope_id).local_symbols()) .collect::>() { - if dependency_graph.symbol_usages(symbol_id) > 0 - || module.exported_symbols.contains(&symbol_id) + if dependency_graph.symbol_usages(symbol_id) > 0 || ignored_symbols.contains(&symbol_id) { used_symbols.insert(symbol_id); continue; @@ -90,7 +87,7 @@ impl SymbolTable { } for type_id in db.named_types() { - if used_types.contains(&type_id) || module.exported_types.contains(&type_id) { + if used_types.contains(&type_id) || ignored_types.contains(&type_id) { continue; } diff --git a/crates/rue-compiler/src/lib.rs b/crates/rue-compiler/src/lib.rs index 9b4e8ce..31868f8 100644 --- a/crates/rue-compiler/src/lib.rs +++ b/crates/rue-compiler/src/lib.rs @@ -39,12 +39,17 @@ pub fn analyze(root: &Root) -> Vec { let mut db = Database::default(); let mut ctx = setup_compiler(&mut db); - load_standard_library(&mut ctx); + let stdlib = load_standard_library(&mut ctx); let main_module_id = load_module(&mut ctx, root); let symbol_table = compile_modules(ctx); try_export_main(&mut db, main_module_id); - build_graph(&mut db, &symbol_table, main_module_id); + build_graph( + &mut db, + &symbol_table, + main_module_id, + &[main_module_id, stdlib], + ); db.diagnostics().to_vec() } @@ -53,12 +58,17 @@ pub fn compile(allocator: &mut Allocator, root: &Root, should_codegen: bool) -> let mut db = Database::default(); let mut ctx = setup_compiler(&mut db); - load_standard_library(&mut ctx); + let stdlib = load_standard_library(&mut ctx); let main_module_id = load_module(&mut ctx, root); let symbol_table = compile_modules(ctx); let main = try_export_main(&mut db, main_module_id).expect("missing main function"); - let graph = build_graph(&mut db, &symbol_table, main_module_id); + let graph = build_graph( + &mut db, + &symbol_table, + main_module_id, + &[main_module_id, stdlib], + ); Output { diagnostics: db.diagnostics().to_vec(), diff --git a/std/stdlib.rue b/std/stdlib.rue index e8f0299..a3db2ed 100644 --- a/std/stdlib.rue +++ b/std/stdlib.rue @@ -1,4 +1,4 @@ -fun tree_hash(value: Any) -> Bytes32 { +export fun tree_hash(value: Any) -> Bytes32 { if value is Bytes { tree_hash_atom(value) } else { @@ -6,10 +6,18 @@ fun tree_hash(value: Any) -> Bytes32 { } } -inline fun tree_hash_atom(value: Bytes) -> Bytes32 { +export inline fun tree_hash_atom(value: Bytes) -> Bytes32 { sha256(1 as Bytes + value) } -inline fun tree_hash_pair(first: Bytes32, rest: Bytes32) -> Bytes32 { +export inline fun tree_hash_pair(first: Bytes32, rest: Bytes32) -> Bytes32 { sha256(2 as Bytes + first + rest) } + +export fun another_something() -> Int { + 42 +} + +fun something() -> Int { + 42 +}