First pass getting deeper LSP into IDE
This commit is contained in:
@@ -1,6 +1,8 @@
|
|||||||
|
pub mod symbols;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test;
|
mod test;
|
||||||
mod v1;
|
mod v1;
|
||||||
mod variable_manager;
|
mod variable_manager;
|
||||||
|
|
||||||
|
pub use symbols::{CompilationMetadata, SymbolInfo, SymbolKind, SyscallType};
|
||||||
pub use v1::{CompilationResult, Compiler, CompilerConfig, Error};
|
pub use v1::{CompilationResult, Compiler, CompilerConfig, Error};
|
||||||
|
|||||||
319
rust_compiler/libs/compiler/src/symbols.rs
Normal file
319
rust_compiler/libs/compiler/src/symbols.rs
Normal file
@@ -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<Span>,
|
||||||
|
/// Optional description for tooltips and documentation
|
||||||
|
pub description: Option<Cow<'a, str>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Cow<'a, str>>,
|
||||||
|
/// Type hint for the return type (if applicable)
|
||||||
|
return_type: Option<Cow<'a, str>>,
|
||||||
|
},
|
||||||
|
/// 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<Cow<'a, str>>,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
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::<Vec<_>>()
|
||||||
|
.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<SymbolInfo<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Cow<'a, str>>,
|
||||||
|
span: Option<Span>,
|
||||||
|
) {
|
||||||
|
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<Span>,
|
||||||
|
) {
|
||||||
|
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<Span>) {
|
||||||
|
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<lsp_types::SymbolInformation> {
|
||||||
|
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<lsp_types::CompletionItem> {
|
||||||
|
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)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -145,6 +145,7 @@ struct CompileLocation<'a> {
|
|||||||
pub struct CompilationResult<'a> {
|
pub struct CompilationResult<'a> {
|
||||||
pub errors: Vec<Error<'a>>,
|
pub errors: Vec<Error<'a>>,
|
||||||
pub instructions: Instructions<'a>,
|
pub instructions: Instructions<'a>,
|
||||||
|
pub metadata: crate::CompilationMetadata<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Metadata for the currently compiling function
|
/// Metadata for the currently compiling function
|
||||||
@@ -202,6 +203,8 @@ pub struct Compiler<'a> {
|
|||||||
pub source_map: HashMap<usize, Vec<Span>>,
|
pub source_map: HashMap<usize, Vec<Span>>,
|
||||||
/// Accumulative errors from the compilation process
|
/// Accumulative errors from the compilation process
|
||||||
pub errors: Vec<Error<'a>>,
|
pub errors: Vec<Error<'a>>,
|
||||||
|
/// Metadata about symbols encountered during compilation
|
||||||
|
pub metadata: crate::CompilationMetadata<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Compiler<'a> {
|
impl<'a> Compiler<'a> {
|
||||||
@@ -219,6 +222,7 @@ impl<'a> Compiler<'a> {
|
|||||||
loop_stack: Vec::new(),
|
loop_stack: Vec::new(),
|
||||||
source_map: HashMap::new(),
|
source_map: HashMap::new(),
|
||||||
errors: Vec::new(),
|
errors: Vec::new(),
|
||||||
|
metadata: crate::CompilationMetadata::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,6 +241,7 @@ impl<'a> Compiler<'a> {
|
|||||||
return CompilationResult {
|
return CompilationResult {
|
||||||
errors: self.errors,
|
errors: self.errors,
|
||||||
instructions: self.instructions,
|
instructions: self.instructions,
|
||||||
|
metadata: self.metadata,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@@ -245,6 +250,7 @@ impl<'a> Compiler<'a> {
|
|||||||
return CompilationResult {
|
return CompilationResult {
|
||||||
errors: self.errors,
|
errors: self.errors,
|
||||||
instructions: self.instructions,
|
instructions: self.instructions,
|
||||||
|
metadata: self.metadata,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -270,6 +276,7 @@ impl<'a> Compiler<'a> {
|
|||||||
return CompilationResult {
|
return CompilationResult {
|
||||||
errors: self.errors,
|
errors: self.errors,
|
||||||
instructions: self.instructions,
|
instructions: self.instructions,
|
||||||
|
metadata: self.metadata,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -283,6 +290,7 @@ impl<'a> Compiler<'a> {
|
|||||||
CompilationResult {
|
CompilationResult {
|
||||||
errors: self.errors,
|
errors: self.errors,
|
||||||
instructions: self.instructions,
|
instructions: self.instructions,
|
||||||
|
metadata: self.metadata,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user