diff --git a/Cargo.lock b/Cargo.lock index 56ce98d..b7e17ff 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -108,6 +108,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "dashmap" version = "5.5.3" @@ -121,6 +127,20 @@ dependencies = [ "parking_lot_core", ] +[[package]] +name = "dashmap" +version = "6.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" +dependencies = [ + "cfg-if", + "crossbeam-utils", + "hashbrown", + "lock_api", + "once_cell", + "parking_lot_core", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -653,6 +673,8 @@ dependencies = [ name = "reid-lsp" version = "0.1.0" dependencies = [ + "dashmap 6.1.0", + "reid", "socket", "tokio", "tower-lsp", @@ -903,7 +925,7 @@ dependencies = [ "async-trait", "auto_impl", "bytes", - "dashmap", + "dashmap 5.5.3", "futures", "httparse", "lsp-types", diff --git a/libtest.sh b/libtest.sh index 654be8a..19385a0 100755 --- a/libtest.sh +++ b/libtest.sh @@ -16,7 +16,7 @@ BINARY="$(echo $1 | cut -d'.' -f1)"".out" echo $1 -cargo run --example cli $@ && \ +cargo run --example cli $@ && \ ./$BINARY ; echo "Return value: ""$?" ## Command from: clang -v hello.o -o test diff --git a/reid-llvm-lib/examples/libtest.rs b/reid-llvm-lib/examples/libtest.rs index c9fdbec..4733ad1 100644 --- a/reid-llvm-lib/examples/libtest.rs +++ b/reid-llvm-lib/examples/libtest.rs @@ -52,7 +52,5 @@ fn main() { else_b.terminate(TerminatorKind::Ret(add)).unwrap(); - dbg!(&context); - context.compile(None, Vec::new()); } diff --git a/reid-llvm-lib/src/builder.rs b/reid-llvm-lib/src/builder.rs index 3be565b..4161c64 100644 --- a/reid-llvm-lib/src/builder.rs +++ b/reid-llvm-lib/src/builder.rs @@ -223,7 +223,6 @@ impl Builder { unsafe { let mut modules = self.modules.borrow_mut(); let module = modules.get_unchecked_mut(module.0); - dbg!(module.functions.iter().map(|f| f.data.name.clone()).collect::>()); module.functions.iter().find(|f| f.data.name == *name).map(|f| f.value) } } diff --git a/reid-llvm-lib/src/compile.rs b/reid-llvm-lib/src/compile.rs index c0052d0..a06da76 100644 --- a/reid-llvm-lib/src/compile.rs +++ b/reid-llvm-lib/src/compile.rs @@ -123,8 +123,6 @@ impl CompiledModule { let llvm_ir = from_cstring(LLVMPrintModuleToString(self.module_ref)).expect("Unable to print LLVM IR to string"); - println!("{}", llvm_ir); - let mut err = ErrorMessageHolder::null(); LLVMVerifyModule( self.module_ref, diff --git a/reid-lsp/Cargo.toml b/reid-lsp/Cargo.toml index b44d6fa..4eb31c3 100644 --- a/reid-lsp/Cargo.toml +++ b/reid-lsp/Cargo.toml @@ -7,3 +7,5 @@ edition = "2024" socket = "0.0.7" tokio = { version = "1.47.0", features = ["full"] } tower-lsp = "0.20.0" +reid = { path = "../reid", version = "1.0.0-beta.2", registry="gitea-teascade", features=[] } +dashmap = "6.1.0" diff --git a/reid-lsp/client/src/extension.ts b/reid-lsp/client/src/extension.ts index e286bb7..79fa04d 100644 --- a/reid-lsp/client/src/extension.ts +++ b/reid-lsp/client/src/extension.ts @@ -57,6 +57,9 @@ export function activate(context: ExtensionContext) { client.info("hello"); + workspace.onDidOpenTextDocument((e) => { + }); + // Start the client. This will also launch the server client.start(); } diff --git a/reid-lsp/src/main.rs b/reid-lsp/src/main.rs index 8b3b908..d888365 100644 --- a/reid-lsp/src/main.rs +++ b/reid-lsp/src/main.rs @@ -1,19 +1,51 @@ +use std::path::PathBuf; + +use dashmap::DashMap; +use reid::ast::lexer::{FullToken, Position}; +use reid::{compile_module, parse_module}; use tower_lsp::jsonrpc::Result; -use tower_lsp::lsp_types::*; +use tower_lsp::lsp_types::{ + self, CompletionItem, CompletionOptions, CompletionParams, CompletionResponse, Diagnostic, DiagnosticSeverity, + DidChangeTextDocumentParams, DidOpenTextDocumentParams, Hover, HoverContents, HoverParams, HoverProviderCapability, + InitializeParams, InitializeResult, InitializedParams, MarkedString, MessageType, OneOf, Range, ServerCapabilities, + TextDocumentItem, TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions, + WorkspaceFoldersServerCapabilities, WorkspaceServerCapabilities, +}; use tower_lsp::{Client, LanguageServer, LspService, Server}; #[derive(Debug)] struct Backend { client: Client, + tokens: DashMap>, + ast: DashMap, } #[tower_lsp::async_trait] impl LanguageServer for Backend { async fn initialize(&self, _: InitializeParams) -> Result { + self.client + .log_message(MessageType::INFO, "Initializing Reid Language Server") + .await; + + let sync = TextDocumentSyncOptions { + open_close: Some(true), + change: Some(TextDocumentSyncKind::FULL), + will_save: None, + will_save_wait_until: None, + save: None, + }; Ok(InitializeResult { capabilities: ServerCapabilities { hover_provider: Some(HoverProviderCapability::Simple(true)), - completion_provider: Some(CompletionOptions::default()), + completion_provider: Some(CompletionOptions { ..Default::default() }), + text_document_sync: Some(TextDocumentSyncCapability::Options(sync)), + workspace: Some(WorkspaceServerCapabilities { + workspace_folders: Some(WorkspaceFoldersServerCapabilities { + supported: Some(true), + change_notifications: Some(OneOf::Left(true)), + }), + file_operations: None, + }), ..Default::default() }, ..Default::default() @@ -22,13 +54,146 @@ impl LanguageServer for Backend { async fn initialized(&self, _: InitializedParams) { self.client - .log_message(MessageType::INFO, "Reid Language Server initialized!") + .log_message(MessageType::INFO, "Reid Language Server initialized hello!") .await; } async fn shutdown(&self) -> Result<()> { Ok(()) } + + async fn completion(&self, _: CompletionParams) -> Result> { + Ok(Some(CompletionResponse::Array(vec![ + CompletionItem::new_simple("Hello".to_string(), "Some detail".to_string()), + CompletionItem::new_simple("Bye".to_string(), "More detail".to_string()), + ]))) + } + + async fn hover(&self, params: HoverParams) -> Result> { + let path = PathBuf::from(params.text_document_position_params.text_document.uri.path()); + let file_name = path.file_name().unwrap().to_str().unwrap().to_owned(); + let tokens = self.tokens.get(&file_name).unwrap(); + let position = params.text_document_position_params.position; + + self.client + .log_message( + MessageType::INFO, + format!("line {}, col {}", position.line, position.character), + ) + .await; + let token = tokens.iter().find(|tok| { + tok.position.1 == position.line + 1 + && (tok.position.0 <= position.character + 1 + && (tok.position.0 + tok.token.len() as u32) > position.character + 1) + }); + + Ok(Some(Hover { + contents: HoverContents::Scalar(MarkedString::String(format!("{:?}", token))), + range: None, + })) + } + + async fn did_open(&self, params: DidOpenTextDocumentParams) { + self.client.log_message(MessageType::INFO, "opened!").await; + self.recompile(TextDocumentItem { + uri: params.text_document.uri, + language_id: params.text_document.language_id, + version: params.text_document.version, + text: params.text_document.text, + }) + .await + } + + async fn did_change(&self, params: DidChangeTextDocumentParams) { + self.client.log_message(MessageType::INFO, "changed!").await; + self.recompile(TextDocumentItem { + text: params.content_changes[0].text.clone(), + uri: params.text_document.uri, + version: params.text_document.version, + language_id: String::new(), + }) + .await + } +} + +impl Backend { + async fn recompile(&self, params: TextDocumentItem) { + let mut map = Default::default(); + let path = PathBuf::from(params.uri.clone().path()); + let file_name = path.file_name().unwrap().to_str().unwrap().to_owned(); + + let mut reid_error = None; + let mut tokens = None; + + match parse_module(¶ms.text, file_name.clone(), &mut map) { + Ok(module) => { + self.client + .log_message(MessageType::INFO, format!("successfully parsed!")) + .await; + tokens = Some(module.1.clone()); + match compile_module(module.0, module.1, &mut map, Some(path), true) { + Ok(_) => {} + Err(e) => { + reid_error = Some(e); + } + } + } + Err(_) => {} + } + + if let Some(tokens) = &tokens { + if let Some(reid_error) = reid_error { + let mut diagnostics = Vec::new(); + for error in reid_error.errors { + let meta = error.get_meta(); + let positions = meta + .range + .into_position(&tokens) + .unwrap_or((Position(0, 0), Position(0, 0))); + self.client.log_message(MessageType::INFO, format!("{:?}", &meta)).await; + self.client + .log_message(MessageType::INFO, format!("{:?}", &tokens)) + .await; + self.client + .log_message(MessageType::INFO, format!("{:?}", &positions)) + .await; + + diagnostics.push(Diagnostic { + range: Range { + start: lsp_types::Position { + line: ((positions.0.1 as i32) - 1).max(0) as u32, + character: ((positions.0.0 as i32) - 1).max(0) as u32, + }, + end: lsp_types::Position { + line: ((positions.1.1 as i32) - 1).max(0) as u32, + character: ((positions.1.0 as i32) - 1).max(0) as u32, + }, + }, + severity: Some(DiagnosticSeverity::ERROR), + code: None, + code_description: None, + source: Some(error.get_type_str().to_owned()), + message: format!("{}", error), + related_information: None, + tags: None, + data: None, + }); + self.client.log_message(MessageType::INFO, format!("{}", error)).await; + } + self.client + .publish_diagnostics(params.uri.clone(), diagnostics, Some(params.version)) + .await; + } else { + self.client + .publish_diagnostics(params.uri.clone(), Vec::new(), Some(params.version)) + .await; + } + } + + if let Some(tokens) = tokens.take() { + self.tokens.insert(file_name.clone(), tokens); + } + } } #[tokio::main] @@ -36,6 +201,10 @@ async fn main() { let stdin = tokio::io::stdin(); let stdout = tokio::io::stdout(); - let (service, socket) = LspService::new(|client| Backend { client }); + let (service, socket) = LspService::new(|client| Backend { + client, + ast: DashMap::new(), + tokens: DashMap::new(), + }); Server::new(stdin, stdout, socket).serve(service).await; } diff --git a/reid/Cargo.toml b/reid/Cargo.toml index 4ab2991..bbd577b 100644 --- a/reid/Cargo.toml +++ b/reid/Cargo.toml @@ -10,6 +10,7 @@ edition = "2021" default = ["color"] color = ["colored"] +log_output = [] [dependencies] ## Make it easier to generate errors diff --git a/reid/examples/cli.rs b/reid/examples/cli.rs index a39a9c4..24981f3 100644 --- a/reid/examples/cli.rs +++ b/reid/examples/cli.rs @@ -12,7 +12,6 @@ fn main() -> Result<(), std::io::Error> { libraries.push(libname); } - dbg!(&filename); let path = PathBuf::from(filename).canonicalize().unwrap(); let parent = path.with_extension(""); let llvm_ir_path = parent.with_extension("ll"); @@ -21,6 +20,7 @@ fn main() -> Result<(), std::io::Error> { let mir_path = parent.with_extension("mir"); let asm_path = parent.with_extension("asm"); + #[cfg(feature = "log_output")] let before = std::time::SystemTime::now(); let text = fs::read_to_string(&path)?; @@ -31,33 +31,39 @@ fn main() -> Result<(), std::io::Error> { match compile_simple(&text, PathBuf::from(&path), Some(cpu), vec![features]) { Ok(( CompileOutput { - triple, + triple: _triple, assembly, obj_buffer, - llvm_ir, + llvm_ir: _llvm_ir, }, CustomIRs { llir, mir }, )) => { - println!("{}", llvm_ir); + #[cfg(feature = "log_output")] + { + println!("{}", _llvm_ir); + println!("Compiled with triple: {}\n", &_triple); + println!("Output LLVM IR to {:?}", llvm_ir_path); + println!("Output Assembly to {:?}", asm_path); + println!("Output Object-file to {:?}\n", object_path); + println!("Output LLIR-file to {:?}\n", llir_path); + println!("Output MIR-file to {:?}\n", mir_path); + } - let after = std::time::SystemTime::now(); - println!("Compiled with triple: {}\n", &triple); - fs::write(&llvm_ir_path, &llvm_ir).expect("Could not write LLVM IR -file!"); - println!("Output LLVM IR to {:?}", llvm_ir_path); + fs::write(&llvm_ir_path, &_llvm_ir).expect("Could not write LLVM IR -file!"); fs::write(&asm_path, &assembly).expect("Could not write Assembly-file!"); - println!("Output Assembly to {:?}", asm_path); fs::write(&object_path, &obj_buffer).expect("Could not write Object-file!"); - println!("Output Object-file to {:?}\n", object_path); fs::write(&llir_path, &llir).expect("Could not write LLIR-file!"); - println!("Output LLIR-file to {:?}\n", llir_path); fs::write(&mir_path, &mir).expect("Could not write MIR-file!"); - println!("Output MIR-file to {:?}\n", mir_path); - println!( - "Compilation took: {:.2}ms\n", - (after.duration_since(before).unwrap().as_micros() as f32) / 1000. - ); + #[cfg(feature = "log_output")] + { + let after = std::time::SystemTime::now(); + println!( + "Compilation took: {:.2}ms\n", + (after.duration_since(before).unwrap().as_micros() as f32) / 1000. + ); - println!("Linking {:?}", &object_path); + println!("Linking {:?}", &object_path); + } let linker = std::env::var("LD").unwrap_or("ld".to_owned()); let mut linker = LDRunner::from_command(&linker).with_library("c"); @@ -69,6 +75,7 @@ fn main() -> Result<(), std::io::Error> { Err(e) => panic!("{}", e), }; } else { + #[cfg(feature = "log_output")] println!("Please input compiled file path!") } Ok(()) diff --git a/reid/src/ast/token_stream.rs b/reid/src/ast/token_stream.rs index b3c2516..607b16f 100644 --- a/reid/src/ast/token_stream.rs +++ b/reid/src/ast/token_stream.rs @@ -42,13 +42,11 @@ impl<'a, 'b> TokenStream<'a, 'b> { /// Useful in conjunction with [`TokenStream::next`] pub fn expecting_err>(&mut self, expected: T) -> Result { let next_token = self.peek().unwrap_or(Token::Eof); + let pos = self.next_token(self.position).0; Ok(Error::Expected( expected.into(), next_token, - TokenRange { - start: self.position, - end: self.position, - }, + TokenRange { start: pos, end: pos }, )) } @@ -173,7 +171,7 @@ impl<'a, 'b> TokenStream<'a, 'b> { pub fn get_range_prev(&self) -> Option { self.ref_position.as_ref().map(|ref_pos| TokenRange { start: **ref_pos, - end: self.position - 1, + end: self.previous_token(self.position).0, }) } diff --git a/reid/src/codegen/mod.rs b/reid/src/codegen/mod.rs index 5e60dfc..2a1fa52 100644 --- a/reid/src/codegen/mod.rs +++ b/reid/src/codegen/mod.rs @@ -728,7 +728,6 @@ impl mir::Statement { mir::StmtKind::Let(NamedVariableRef(ty, name, meta), mutable, expression) => { let value = expression.codegen(scope, &state)?.unwrap(); - dbg!(&scope.allocator, &meta, &value.1); let alloca = scope .allocate(meta, &value.1) .unwrap() diff --git a/reid/src/error_raporting.rs b/reid/src/error_raporting.rs index acff13a..a60400b 100644 --- a/reid/src/error_raporting.rs +++ b/reid/src/error_raporting.rs @@ -50,7 +50,7 @@ impl ErrorKind { } impl ErrorKind { - fn get_meta(&self) -> Metadata { + pub fn get_meta(&self) -> Metadata { match &self { ErrorKind::LexerError(error) => error.metadata, ErrorKind::ParserError(error) => error.metadata, @@ -63,6 +63,18 @@ impl ErrorKind { ErrorKind::MacroError(error) => error.metadata, } } + + pub fn get_type_str(&self) -> &str { + match self { + ErrorKind::LexerError(_) => "lexer", + ErrorKind::ParserError(_) => "parser", + ErrorKind::TypeCheckError(_) => "typechecker", + ErrorKind::TypeInferenceError(_) => "type-inferrer", + ErrorKind::LinkerError(_) => "linker", + ErrorKind::MacroError(_) => "macro-pass", + ErrorKind::CodegenError(_) => "codegen", + } + } } impl PartialOrd for ErrorKind { @@ -120,7 +132,7 @@ impl ErrorModules { #[derive(Debug, Clone, PartialEq)] pub struct ReidError { map: ErrorModules, - errors: Vec, + pub errors: Vec, } impl ReidError { @@ -185,9 +197,7 @@ impl std::fmt::Display for ReidError { let module = self.map.module(&meta.source_module_id); let position = if let Some(module) = module { if let Some(tokens) = &module.tokens { - let range_tokens = meta.range.into_tokens(&tokens); - - get_position(&range_tokens).or(meta.position.map(|p| (p, p))) + meta.range.into_position(tokens).or(meta.position.map(|p| (p, p))) } else if let Some(position) = meta.position { Some((position, position)) } else { @@ -237,6 +247,11 @@ impl TokenRange { .take(self.end + 1 - self.start) .collect::>() } + + pub fn into_position<'v>(&self, tokens: &'v Vec) -> Option<(Position, Position)> { + let tokens = self.into_tokens(tokens); + get_position(&tokens) + } } fn get_position(tokens: &Vec<&FullToken>) -> Option<(Position, Position)> { diff --git a/reid/src/ld.rs b/reid/src/ld.rs index 7d675fc..9873c66 100644 --- a/reid/src/ld.rs +++ b/reid/src/ld.rs @@ -26,12 +26,11 @@ impl LDRunner { let dyn_linker_path = find_objectfile(&self.dynamic_linker); let crt1_path = find_objectfile("crt1.o"); + #[cfg(feature = "log_output")] println!("LDRunner: Using dynamic linker at: {:?}", dyn_linker_path); let mut ld = Command::new(&self.command); - ld.arg("-dynamic-linker") - .arg(dyn_linker_path) - .arg(crt1_path); + ld.arg("-dynamic-linker").arg(dyn_linker_path).arg(crt1_path); for library in &self.libraries { ld.arg(format!("-l{}", library)); @@ -41,22 +40,21 @@ impl LDRunner { .arg("-o") .arg(out_path.to_str().unwrap()); + #[cfg(feature = "log_output")] println!( "LDRunner: Executing linker to objfile at {:?} => {:?}", input_path, out_path ); + #[cfg(feature = "log_output")] dbg!(&ld); ld.spawn().expect("Unable to execute ld!"); thread::sleep(Duration::from_millis(100)); + #[cfg(feature = "log_output")] println!("Setting executable bit to {:?}..", out_path); - Command::new("chmod") - .arg("+x") - .arg(out_path) - .spawn() - .unwrap(); + Command::new("chmod").arg("+x").arg(out_path).spawn().unwrap(); thread::sleep(Duration::from_millis(100)); } } diff --git a/reid/src/lib.rs b/reid/src/lib.rs index 8acf343..ce6ec5f 100644 --- a/reid/src/lib.rs +++ b/reid/src/lib.rs @@ -72,7 +72,7 @@ use crate::{ mir::macros::{form_macros, MacroModule, MacroPass}, }; -mod ast; +pub mod ast; mod codegen; pub mod error_raporting; pub mod ld; @@ -93,6 +93,7 @@ pub fn parse_module<'map, T: Into>( map.set_tokens(id, tokens.clone()); #[cfg(debug_assertions)] + #[cfg(feature = "log_output")] println!("{:#?}", &tokens); Ok((id, tokens)) @@ -127,6 +128,7 @@ pub fn compile_module<'map>( }; #[cfg(debug_assertions)] + #[cfg(feature = "log_output")] dbg!(&ast_module); Ok(ast_module.process(module_id)) @@ -137,9 +139,11 @@ pub fn perform_all_passes<'map>( module_map: &'map mut ErrorModules, ) -> Result<(), ReidError> { #[cfg(debug_assertions)] + #[cfg(feature = "log_output")] dbg!(&context); #[cfg(debug_assertions)] + #[cfg(feature = "log_output")] println!("{:#}", &context); let state = context.pass(&mut LinkerPass { @@ -154,10 +158,13 @@ pub fn perform_all_passes<'map>( } #[cfg(debug_assertions)] + #[cfg(feature = "log_output")] println!("{:-^100}", "LINKER OUTPUT"); #[cfg(debug_assertions)] + #[cfg(feature = "log_output")] println!("{:#}", &context); #[cfg(debug_assertions)] + #[cfg(feature = "log_output")] dbg!(&state); if !state.errors.is_empty() { @@ -179,10 +186,13 @@ pub fn perform_all_passes<'map>( let state = context.pass(&mut macro_pass)?; #[cfg(debug_assertions)] + #[cfg(feature = "log_output")] println!("{:-^100}", "MACRO OUTPUT"); #[cfg(debug_assertions)] + #[cfg(feature = "log_output")] println!("{:#}", &context); #[cfg(debug_assertions)] + #[cfg(feature = "log_output")] dbg!(&state); if !state.errors.is_empty() { @@ -217,12 +227,16 @@ pub fn perform_all_passes<'map>( let state = context.pass(&mut TypeInference { refs: &mut refs })?; #[cfg(debug_assertions)] + #[cfg(feature = "log_output")] println!("{:-^100}", "TYPE INFERRER OUTPUT"); #[cfg(debug_assertions)] + #[cfg(feature = "log_output")] println!("{}", &refs); #[cfg(debug_assertions)] + #[cfg(feature = "log_output")] println!("{:#}", &context); #[cfg(debug_assertions)] + #[cfg(feature = "log_output")] dbg!(&state); if !state.errors.is_empty() { @@ -239,10 +253,13 @@ pub fn perform_all_passes<'map>( let state = context.pass(&mut TypeCheck { refs: &refs })?; #[cfg(debug_assertions)] + #[cfg(feature = "log_output")] println!("{:-^100}", "TYPECHECKER OUTPUT"); #[cfg(debug_assertions)] + #[cfg(feature = "log_output")] println!("{:#}", &context); #[cfg(debug_assertions)] + #[cfg(feature = "log_output")] dbg!(&state); if !state.errors.is_empty() { @@ -280,8 +297,10 @@ pub fn compile_and_pass<'map>( perform_all_passes(&mut mir_context, module_map)?; #[cfg(debug_assertions)] + #[cfg(feature = "log_output")] println!("{:-^100}", "FINAL OUTPUT"); #[cfg(debug_assertions)] + #[cfg(feature = "log_output")] println!("{:#}", &mir_context); let mut context = Context::new(format!("Reid ({})", env!("CARGO_PKG_VERSION"))); @@ -291,6 +310,7 @@ pub fn compile_and_pass<'map>( }; #[cfg(debug_assertions)] + #[cfg(feature = "log_output")] println!("{}", &codegen_modules.context); let compiled = codegen_modules.compile(cpu, features); diff --git a/reid/src/mir/mod.rs b/reid/src/mir/mod.rs index 3736d0e..e4c3054 100644 --- a/reid/src/mir/mod.rs +++ b/reid/src/mir/mod.rs @@ -41,16 +41,7 @@ impl Metadata { } pub fn into_positions(&self, tokens: &Vec) -> Option<(Position, Position)> { - let mut iter = tokens - .iter() - .skip(self.range.start) - .take(self.range.end - self.range.start); - if let Some(first) = iter.next() { - let last = iter.last().unwrap_or(first); - Some((first.position, last.position.add(last.token.len() as u32))) - } else { - None - } + self.range.into_position(tokens) } }