From 6dc4342ac31b601c260ad2f68fbad9c2de9dfe76 Mon Sep 17 00:00:00 2001 From: Devin Bidwell Date: Fri, 2 Jan 2026 16:57:06 -0700 Subject: [PATCH] First pass getting deeper LSP into IDE --- rust_compiler/libs/compiler/src/lib.rs | 2 + rust_compiler/libs/compiler/src/symbols.rs | 319 +++++++++++++++++++++ rust_compiler/libs/compiler/src/v1.rs | 8 + 3 files changed, 329 insertions(+) create mode 100644 rust_compiler/libs/compiler/src/symbols.rs diff --git a/rust_compiler/libs/compiler/src/lib.rs b/rust_compiler/libs/compiler/src/lib.rs index 34f0c8b..aa623be 100644 --- a/rust_compiler/libs/compiler/src/lib.rs +++ b/rust_compiler/libs/compiler/src/lib.rs @@ -1,6 +1,8 @@ +pub mod symbols; #[cfg(test)] mod test; mod v1; mod variable_manager; +pub use symbols::{CompilationMetadata, SymbolInfo, SymbolKind, SyscallType}; pub use v1::{CompilationResult, Compiler, CompilerConfig, Error}; diff --git a/rust_compiler/libs/compiler/src/symbols.rs b/rust_compiler/libs/compiler/src/symbols.rs new file mode 100644 index 0000000..af0569c --- /dev/null +++ b/rust_compiler/libs/compiler/src/symbols.rs @@ -0,0 +1,319 @@ +use helpers::Span; +use std::borrow::Cow; + +/// Represents a symbol (function, syscall, variable, etc.) that can be referenced in code. +/// Designed to be LSP-compatible for easy integration with language servers. +#[derive(Debug, Clone)] +pub struct SymbolInfo<'a> { + /// The name of the symbol + pub name: Cow<'a, str>, + /// The kind of symbol and associated metadata + pub kind: SymbolKind<'a>, + /// The source location of this symbol (for IDE features) + pub span: Option, + /// Optional description for tooltips and documentation + pub description: Option>, +} + +impl<'a> SymbolInfo<'a> { + /// Converts to an LSP SymbolInformation for protocol compatibility. + pub fn to_lsp_symbol_information(&self, uri: lsp_types::Uri) -> lsp_types::SymbolInformation { + lsp_types::SymbolInformation { + name: self.name.to_string(), + kind: self.kind.to_lsp_symbol_kind(), + deprecated: None, + location: lsp_types::Location { + uri, + range: self.span.as_ref().map(|s| (*s).into()).unwrap_or_default(), + }, + container_name: None, + tags: None, + } + } + + /// Converts to an LSP CompletionItem for autocomplete. + pub fn to_lsp_completion_item(&self) -> lsp_types::CompletionItem { + lsp_types::CompletionItem { + label: self.name.to_string(), + kind: Some(self.kind.to_lsp_completion_kind()), + documentation: self + .description + .as_ref() + .map(|d| lsp_types::Documentation::String(d.to_string())), + detail: Some(self.kind.detail_string()), + ..Default::default() + } + } +} + +/// Discriminates between different kinds of symbols. +#[derive(Debug, Clone)] +pub enum SymbolKind<'a> { + /// A user-defined function + Function { + /// Names of parameters in order + parameters: Vec>, + /// Type hint for the return type (if applicable) + return_type: Option>, + }, + /// A system or math syscall + Syscall { + /// Whether it's a System or Math syscall + syscall_type: SyscallType, + /// Number of expected arguments + argument_count: usize, + }, + /// A variable declaration + Variable { + /// Type hint for the variable (if applicable) + type_hint: Option>, + }, +} + +impl<'a> SymbolKind<'a> { + /// Converts to LSP SymbolKind for protocol compatibility. + fn to_lsp_symbol_kind(&self) -> lsp_types::SymbolKind { + match self { + SymbolKind::Function { .. } => lsp_types::SymbolKind::FUNCTION, + SymbolKind::Syscall { .. } => lsp_types::SymbolKind::FUNCTION, // Syscalls are function-like + SymbolKind::Variable { .. } => lsp_types::SymbolKind::VARIABLE, + } + } + + /// Converts to LSP CompletionItemKind for autocomplete filtering. + fn to_lsp_completion_kind(&self) -> lsp_types::CompletionItemKind { + match self { + SymbolKind::Function { .. } => lsp_types::CompletionItemKind::FUNCTION, + SymbolKind::Syscall { .. } => lsp_types::CompletionItemKind::FUNCTION, + SymbolKind::Variable { .. } => lsp_types::CompletionItemKind::VARIABLE, + } + } + + /// Returns a human-readable detail string for display in IDEs. + fn detail_string(&self) -> String { + match self { + SymbolKind::Function { + parameters, + return_type, + } => { + let params = parameters + .iter() + .map(|p| p.to_string()) + .collect::>() + .join(", "); + let ret = return_type + .as_ref() + .map(|t| format!(" -> {}", t)) + .unwrap_or_default(); + format!("fn({}){}", params, ret) + } + SymbolKind::Syscall { + syscall_type, + argument_count, + } => { + format!( + "{}(... {} args)", + match syscall_type { + SyscallType::System => "syscall", + SyscallType::Math => "math", + }, + argument_count + ) + } + SymbolKind::Variable { type_hint } => type_hint + .as_ref() + .map(|t| t.to_string()) + .unwrap_or_else(|| "var".to_string()), + } + } +} + +/// Distinguishes between System and Math syscalls. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum SyscallType { + System, + Math, +} + +/// Metadata collected during compilation, including all referenced symbols. +#[derive(Debug, Default)] +pub struct CompilationMetadata<'a> { + /// All symbols encountered during compilation (functions, syscalls, variables) + pub symbols: Vec>, +} + +impl<'a> CompilationMetadata<'a> { + /// Creates a new empty compilation metadata. + pub fn new() -> Self { + Self { + symbols: Vec::new(), + } + } + + /// Adds a symbol to the metadata. + pub fn add_symbol(&mut self, symbol: SymbolInfo<'a>) { + self.symbols.push(symbol); + } + + /// Adds a function symbol. + pub fn add_function( + &mut self, + name: Cow<'a, str>, + parameters: Vec>, + span: Option, + ) { + self.add_symbol(SymbolInfo { + name, + kind: SymbolKind::Function { + parameters, + return_type: None, + }, + span, + description: None, + }); + } + + /// Adds a syscall symbol. + pub fn add_syscall( + &mut self, + name: Cow<'a, str>, + syscall_type: SyscallType, + argument_count: usize, + span: Option, + ) { + self.add_symbol(SymbolInfo { + name, + kind: SymbolKind::Syscall { + syscall_type, + argument_count, + }, + span, + description: None, + }); + } + + /// Adds a variable symbol. + pub fn add_variable(&mut self, name: Cow<'a, str>, span: Option) { + self.add_symbol(SymbolInfo { + name, + kind: SymbolKind::Variable { type_hint: None }, + span, + description: None, + }); + } + + /// Returns all symbols of a specific kind. + pub fn symbols_of_kind(&self, kind: &str) -> Vec<&SymbolInfo<'a>> { + self.symbols + .iter() + .filter(|sym| match (&sym.kind, kind) { + (SymbolKind::Function { .. }, "function") => true, + (SymbolKind::Syscall { .. }, "syscall") => true, + (SymbolKind::Variable { .. }, "variable") => true, + _ => false, + }) + .collect() + } + + /// Converts all symbols to LSP SymbolInformation for protocol compatibility. + pub fn to_lsp_symbols(&self, uri: lsp_types::Uri) -> Vec { + self.symbols + .iter() + .map(|sym| sym.to_lsp_symbol_information(uri.clone())) + .collect() + } + + /// Converts all symbols to LSP CompletionItems for autocomplete. + pub fn to_lsp_completion_items(&self) -> Vec { + self.symbols + .iter() + .map(|sym| sym.to_lsp_completion_item()) + .collect() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_metadata_creation() { + let metadata = CompilationMetadata::new(); + assert!(metadata.symbols.is_empty()); + } + + #[test] + fn test_add_function_symbol() { + let mut metadata = CompilationMetadata::new(); + metadata.add_function("test_func".into(), vec!["x".into(), "y".into()], None); + assert_eq!(metadata.symbols.len(), 1); + assert_eq!(metadata.symbols[0].name, "test_func"); + } + + #[test] + fn test_add_syscall_symbol() { + let mut metadata = CompilationMetadata::new(); + metadata.add_syscall("hash".into(), SyscallType::System, 1, None); + assert_eq!(metadata.symbols.len(), 1); + assert_eq!(metadata.symbols[0].name, "hash"); + } + + #[test] + fn test_symbols_of_kind() { + let mut metadata = CompilationMetadata::new(); + metadata.add_function("func1".into(), vec![], None); + metadata.add_syscall("hash".into(), SyscallType::System, 1, None); + metadata.add_variable("x".into(), None); + + let functions = metadata.symbols_of_kind("function"); + assert_eq!(functions.len(), 1); + + let syscalls = metadata.symbols_of_kind("syscall"); + assert_eq!(syscalls.len(), 1); + + let variables = metadata.symbols_of_kind("variable"); + assert_eq!(variables.len(), 1); + } + + #[test] + #[ignore] // Requires complex Uri construction + fn test_lsp_symbol_conversion() { + let mut metadata = CompilationMetadata::new(); + metadata.add_function("test_func".into(), vec!["a".into(), "b".into()], None); + + // In real usage with LSP, Uri would be passed from the server + // This test demonstrates the conversion method exists and is type-safe + } + + #[test] + fn test_lsp_completion_items() { + let mut metadata = CompilationMetadata::new(); + metadata.add_function("test_func".into(), vec![], None); + metadata.add_syscall("hash".into(), SyscallType::System, 1, None); + metadata.add_variable("x".into(), None); + + let completions = metadata.to_lsp_completion_items(); + assert_eq!(completions.len(), 3); + + // Verify function + assert_eq!(completions[0].label, "test_func"); + assert_eq!( + completions[0].kind, + Some(lsp_types::CompletionItemKind::FUNCTION) + ); + + // Verify syscall + assert_eq!(completions[1].label, "hash"); + assert_eq!( + completions[1].kind, + Some(lsp_types::CompletionItemKind::FUNCTION) + ); + + // Verify variable + assert_eq!(completions[2].label, "x"); + assert_eq!( + completions[2].kind, + Some(lsp_types::CompletionItemKind::VARIABLE) + ); + } +} diff --git a/rust_compiler/libs/compiler/src/v1.rs b/rust_compiler/libs/compiler/src/v1.rs index 9a0934f..731d12d 100644 --- a/rust_compiler/libs/compiler/src/v1.rs +++ b/rust_compiler/libs/compiler/src/v1.rs @@ -145,6 +145,7 @@ struct CompileLocation<'a> { pub struct CompilationResult<'a> { pub errors: Vec>, pub instructions: Instructions<'a>, + pub metadata: crate::CompilationMetadata<'a>, } /// Metadata for the currently compiling function @@ -202,6 +203,8 @@ pub struct Compiler<'a> { pub source_map: HashMap>, /// Accumulative errors from the compilation process pub errors: Vec>, + /// Metadata about symbols encountered during compilation + pub metadata: crate::CompilationMetadata<'a>, } impl<'a> Compiler<'a> { @@ -219,6 +222,7 @@ impl<'a> Compiler<'a> { loop_stack: Vec::new(), source_map: HashMap::new(), errors: Vec::new(), + metadata: crate::CompilationMetadata::new(), } } @@ -237,6 +241,7 @@ impl<'a> Compiler<'a> { return CompilationResult { errors: self.errors, instructions: self.instructions, + metadata: self.metadata, }; } Err(e) => { @@ -245,6 +250,7 @@ impl<'a> Compiler<'a> { return CompilationResult { errors: self.errors, instructions: self.instructions, + metadata: self.metadata, }; } }; @@ -270,6 +276,7 @@ impl<'a> Compiler<'a> { return CompilationResult { errors: self.errors, instructions: self.instructions, + metadata: self.metadata, }; } @@ -283,6 +290,7 @@ impl<'a> Compiler<'a> { CompilationResult { errors: self.errors, instructions: self.instructions, + metadata: self.metadata, } }