diff --git a/reid-lsp/src/analysis.rs b/reid-lsp/src/analysis.rs new file mode 100644 index 0000000..fc0fe41 --- /dev/null +++ b/reid-lsp/src/analysis.rs @@ -0,0 +1,294 @@ +use std::{collections::HashMap, path::PathBuf}; + +use dashmap::DashMap; +use reid::{ + ast::lexer::FullToken, + compile_module, + error_raporting::{ErrorModules, ReidError}, + mir::{self, Context, FunctionCall, IfExpression, SourceModuleId, StructType, TypeKind, WhileStatement}, + perform_all_passes, +}; + +use crate::CompileResult; + +#[derive(Debug, Clone)] +pub struct StaticAnalysis { + pub tokens: Vec, + pub token_analysis: HashMap, + pub error: Option, +} + +#[derive(Debug, Clone)] +pub struct SemanticAnalysis { + pub ty: Option, +} + +pub fn analyze( + module_id: SourceModuleId, + tokens: Vec, + path: PathBuf, + map: &mut ErrorModules, +) -> Result, ReidError> { + let mut token_analysis = HashMap::new(); + + let (module, error) = match compile_module(module_id, tokens, map, Some(path.clone()), true)? { + Ok(module) => (module, None), + Err((m, err)) => (m.process(module_id), Some(err)), + }; + + let module_id = module.module_id; + let mut context = Context::from(vec![module], path.parent().unwrap().to_owned()); + perform_all_passes(&mut context, map)?; + + for module in context.modules.into_values() { + if module.module_id != module_id { + continue; + } + for idx in 0..module.tokens.len() { + token_analysis.insert( + idx, + SemanticAnalysis { + ty: find_type_in_context(&module, idx), + }, + ); + } + + return Ok(Some(StaticAnalysis { + tokens: module.tokens, + token_analysis: token_analysis, + error, + })); + } + return Ok(None); +} + +pub fn find_type_in_context(module: &mir::Module, token_idx: usize) -> Option { + for import in &module.imports { + if import.1.contains(token_idx) { + return Some(TypeKind::CustomType(mir::CustomTypeKey( + "d".to_owned(), + SourceModuleId(1), + ))); + } + } + for typedef in &module.typedefs { + if !typedef.meta.contains(token_idx) { + continue; + } + + match &typedef.kind { + mir::TypeDefinitionKind::Struct(StructType(fields)) => { + for field in fields { + if field.2.contains(token_idx) { + return Some(field.1.clone()); + } + } + } + } + } + + for binop in &module.binop_defs { + if let Some(meta) = binop.block_meta() { + if !meta.contains(token_idx) { + continue; + } + } else { + continue; + } + + return match &binop.fn_kind { + mir::FunctionDefinitionKind::Local(block, _) => find_type_in_block(&block, module.module_id, token_idx), + mir::FunctionDefinitionKind::Extern(_) => None, + mir::FunctionDefinitionKind::Intrinsic(_) => None, + }; + } + + for (_, function) in &module.associated_functions { + if !(function.signature() + function.block_meta()).contains(token_idx) { + continue; + } + + for param in &function.parameters { + if param.meta.contains(token_idx) { + return Some(param.ty.clone()); + } + } + + return match &function.kind { + mir::FunctionDefinitionKind::Local(block, _) => find_type_in_block(&block, module.module_id, token_idx), + mir::FunctionDefinitionKind::Extern(_) => None, + mir::FunctionDefinitionKind::Intrinsic(_) => None, + }; + } + + for function in &module.functions { + if !(function.signature() + function.block_meta()).contains(token_idx) { + continue; + } + + for param in &function.parameters { + if param.meta.contains(token_idx) { + return Some(param.ty.clone()); + } + } + + return match &function.kind { + mir::FunctionDefinitionKind::Local(block, _) => find_type_in_block(&block, module.module_id, token_idx), + mir::FunctionDefinitionKind::Extern(_) => None, + mir::FunctionDefinitionKind::Intrinsic(_) => None, + }; + } + None +} + +pub fn find_type_in_block(block: &mir::Block, module_id: SourceModuleId, token_idx: usize) -> Option { + if !block.meta.contains(token_idx) { + return Some(TypeKind::Bool); + } + + for statement in &block.statements { + if !statement.1.contains(token_idx) { + continue; + } + match &statement.0 { + mir::StmtKind::Let(named_variable_ref, _, expression) => { + if named_variable_ref.2.contains(token_idx) { + return expression + .return_type(&Default::default(), module_id) + .ok() + .map(|(_, ty)| ty); + } else { + return find_type_in_expr(&expression, module_id, token_idx); + } + } + mir::StmtKind::Set(lhs, rhs) => { + return find_type_in_expr(lhs, module_id, token_idx).or(find_type_in_expr(rhs, module_id, token_idx)); + } + mir::StmtKind::Import(_) => {} + mir::StmtKind::Expression(expression) => return find_type_in_expr(expression, module_id, token_idx), + mir::StmtKind::While(WhileStatement { condition, block, .. }) => { + return find_type_in_expr(condition, module_id, token_idx) + .or(find_type_in_block(block, module_id, token_idx)); + } + } + } + + if let Some((_, Some(return_exp))) = &block.return_expression { + if let Some(ty) = find_type_in_expr(return_exp, module_id, token_idx) { + return Some(ty); + } + } + + None +} + +pub fn find_type_in_expr(expr: &mir::Expression, module_id: SourceModuleId, token_idx: usize) -> Option { + if !expr.1.contains(token_idx) { + return None; + } + + match &expr.0 { + mir::ExprKind::Variable(named_variable_ref) => Some(named_variable_ref.0.clone()), + mir::ExprKind::Indexed(value, type_kind, index_expr) => Some( + find_type_in_expr(&value, module_id, token_idx) + .or(find_type_in_expr(&index_expr, module_id, token_idx)) + .unwrap_or(type_kind.clone()), + ), + mir::ExprKind::Accessed(expression, type_kind, _, meta) => { + if meta.contains(token_idx) { + Some(type_kind.clone()) + } else { + find_type_in_expr(&expression, module_id, token_idx) + } + } + mir::ExprKind::Array(expressions) => { + for expr in expressions { + if let Some(ty) = find_type_in_expr(expr, module_id, token_idx) { + return Some(ty); + } + } + None + } + mir::ExprKind::Struct(name, items) => { + for (_, expr, meta) in items { + if meta.contains(token_idx) { + return expr.return_type(&Default::default(), module_id).map(|(_, t)| t).ok(); + } + if let Some(ty) = find_type_in_expr(expr, module_id, token_idx) { + return Some(ty); + } + } + Some(TypeKind::CustomType(mir::CustomTypeKey(name.clone(), module_id))) + } + mir::ExprKind::Literal(literal) => Some(literal.as_type()), + mir::ExprKind::BinOp(_, lhs, rhs, type_kind) => { + if let Some(ty) = find_type_in_expr(lhs, module_id, token_idx) { + return Some(ty); + } + if let Some(ty) = find_type_in_expr(rhs, module_id, token_idx) { + return Some(ty); + } + Some(type_kind.clone()) + } + mir::ExprKind::FunctionCall(FunctionCall { + return_type, + parameters, + .. + }) => { + for expr in parameters { + if let Some(ty) = find_type_in_expr(expr, module_id, token_idx) { + return Some(ty); + } + } + Some(return_type.clone()) + } + mir::ExprKind::AssociatedFunctionCall( + _, + FunctionCall { + return_type, + parameters, + .. + }, + ) => { + for expr in parameters { + if let Some(ty) = find_type_in_expr(expr, module_id, token_idx) { + return Some(ty); + } + } + Some(return_type.clone()) + } + mir::ExprKind::If(IfExpression(cond, then_e, else_e)) => find_type_in_expr(&cond, module_id, token_idx) + .or(find_type_in_expr(&then_e, module_id, token_idx)) + .or(else_e.clone().and_then(|e| find_type_in_expr(&e, module_id, token_idx))), + mir::ExprKind::Block(block) => find_type_in_block(block, module_id, token_idx), + mir::ExprKind::Borrow(expression, mutable) => { + if let Some(ty) = find_type_in_expr(&expression, module_id, token_idx) { + return Some(ty); + } + if let Ok(inner) = expression.return_type(&Default::default(), module_id).map(|(_, ty)| ty) { + Some(TypeKind::Borrow(Box::new(inner.clone()), *mutable)) + } else { + None + } + } + mir::ExprKind::Deref(expression) => { + if let Some(ty) = find_type_in_expr(&expression, module_id, token_idx) { + return Some(ty); + } + if let Ok(TypeKind::Borrow(inner, _)) = + expression.return_type(&Default::default(), module_id).map(|(_, ty)| ty) + { + Some(*inner.clone()) + } else { + Some(TypeKind::CustomType(mir::CustomTypeKey( + "ä".to_owned(), + SourceModuleId(1), + ))) + } + } + mir::ExprKind::CastTo(expression, type_kind) => { + Some(find_type_in_expr(&expression, module_id, token_idx).unwrap_or(type_kind.clone())) + } + mir::ExprKind::GlobalRef(_, type_kind) => Some(type_kind.clone()), + } +} diff --git a/reid-lsp/src/main.rs b/reid-lsp/src/main.rs index 176087b..7312f2c 100644 --- a/reid-lsp/src/main.rs +++ b/reid-lsp/src/main.rs @@ -1,29 +1,28 @@ -use std::collections::HashMap; use std::path::PathBuf; use dashmap::DashMap; use reid::ast::lexer::{FullToken, Position}; -use reid::error_raporting::{ErrorModules, ReidError}; -use reid::mir::{ - self, Context, FunctionCall, FunctionDefinition, FunctionParam, IfExpression, SourceModuleId, StructType, TypeKind, - WhileStatement, -}; -use reid::{compile_module, parse_module, perform_all_passes}; +use reid::error_raporting::{self, ErrorModules, ReidError}; +use reid::mir::{SourceModuleId, TypeKind}; +use reid::parse_module; use tower_lsp::lsp_types::{ self, CompletionItem, CompletionOptions, CompletionParams, CompletionResponse, Diagnostic, DiagnosticSeverity, DidChangeTextDocumentParams, DidOpenTextDocumentParams, Hover, HoverContents, HoverParams, HoverProviderCapability, - InitializeParams, InitializeResult, InitializedParams, MarkedString, MarkupContent, MarkupKind, MessageType, OneOf, - Range, ServerCapabilities, TextDocumentItem, TextDocumentSyncCapability, TextDocumentSyncKind, - TextDocumentSyncOptions, WorkspaceFoldersServerCapabilities, WorkspaceServerCapabilities, + InitializeParams, InitializeResult, InitializedParams, MarkupContent, MarkupKind, MessageType, OneOf, Range, + ServerCapabilities, TextDocumentItem, TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions, + WorkspaceFoldersServerCapabilities, WorkspaceServerCapabilities, }; use tower_lsp::{Client, LanguageServer, LspService, Server, jsonrpc}; +use crate::analysis::{StaticAnalysis, analyze}; + +mod analysis; + #[derive(Debug)] struct Backend { client: Client, - tokens: DashMap>, + analysis: DashMap, ast: DashMap, - types: DashMap>>, } #[tower_lsp::async_trait] @@ -78,11 +77,11 @@ impl LanguageServer for Backend { async fn hover(&self, params: HoverParams) -> jsonrpc::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); + let analysis = self.analysis.get(&file_name); let position = params.text_document_position_params.position; - let token = if let Some(tokens) = &tokens { - tokens.iter().find(|tok| { + let token = if let Some(analysis) = &analysis { + analysis.tokens.iter().enumerate().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) @@ -91,8 +90,8 @@ impl LanguageServer for Backend { None }; - let (range, ty) = if let Some(token) = token { - if let Some(possible_ty) = self.types.get(&file_name).unwrap().get(token) { + let (range, ty) = if let Some((idx, token)) = token { + if let Some(possible_ty) = self.analysis.get(&file_name).unwrap().token_analysis.get(&idx) { let start = token.position; let end = token.position.add(token.token.len() as u32); let range = Range { @@ -105,7 +104,7 @@ impl LanguageServer for Backend { character: (end.0 as i32 - 1).max(0) as u32, }, }; - if let Some(ty) = possible_ty.clone() { + if let Some(ty) = possible_ty.ty.clone() { (Some(range), format!("{}", ty)) } else { (Some(range), String::from("None type")) @@ -154,50 +153,33 @@ impl Backend { let mut map = Default::default(); let parse_res = parse(¶ms.text, path.clone(), &mut map); let (tokens, result) = match parse_res { - Ok((module_id, tokens)) => (tokens.clone(), compile(module_id, tokens, path, &mut map)), + Ok((module_id, tokens)) => (tokens.clone(), analyze(module_id, tokens, path, &mut map)), Err(e) => (Vec::new(), Err(e)), }; let mut diagnostics = Vec::new(); match result { - Ok(Some(result)) => { - self.tokens.insert(file_name.clone(), result.tokens); - self.types.insert(file_name.clone(), result.types); + Ok(Some(mut analysis)) => { + if let Some(reid_error) = &mut analysis.error { + self.client + .log_message( + MessageType::INFO, + format!("Successfully compiled despite parsing errors!"), + ) + .await; + reid_error.errors.dedup(); + for error in &reid_error.errors { + diagnostics.push(reid_error_into_diagnostic(error, &tokens)); + self.client.log_message(MessageType::INFO, format!("{}", error)).await; + } + } + self.analysis.insert(file_name.clone(), analysis); } Ok(_) => {} Err(mut reid_error) => { reid_error.errors.dedup(); - 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!("{:?}", &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, - }); + for error in &reid_error.errors { + diagnostics.push(reid_error_into_diagnostic(error, &tokens)); self.client.log_message(MessageType::INFO, format!("{}", error)).await; } } @@ -209,6 +191,35 @@ impl Backend { } } +fn reid_error_into_diagnostic(error: &error_raporting::ErrorKind, tokens: &Vec) -> Diagnostic { + let meta = error.get_meta(); + let positions = meta + .range + .into_position(&tokens) + .unwrap_or((Position(0, 0), Position(0, 0))); + + 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, + } +} + struct CompileResult { tokens: Vec, types: DashMap>, @@ -220,36 +231,6 @@ fn parse(source: &str, path: PathBuf, map: &mut ErrorModules) -> Result<(SourceM Ok(parse_module(source, file_name.clone(), map)?) } -fn compile( - module_id: SourceModuleId, - tokens: Vec, - path: PathBuf, - map: &mut ErrorModules, -) -> Result, ReidError> { - let token_types = DashMap::new(); - - let module = compile_module(module_id, tokens, map, Some(path.clone()), true)?; - - let module_id = module.module_id; - let mut context = Context::from(vec![module], path.parent().unwrap().to_owned()); - perform_all_passes(&mut context, map)?; - - for module in context.modules.into_values() { - if module.module_id != module_id { - continue; - } - for (idx, token) in module.tokens.iter().enumerate() { - token_types.insert(token.clone(), find_type_in_context(&module, idx)); - } - - return Ok(Some(CompileResult { - tokens: module.tokens, - types: token_types, - })); - } - return Ok(None); -} - #[tokio::main] async fn main() { let stdin = tokio::io::stdin(); @@ -258,239 +239,7 @@ async fn main() { let (service, socket) = LspService::new(|client| Backend { client, ast: DashMap::new(), - tokens: DashMap::new(), - types: DashMap::new(), + analysis: DashMap::new(), }); Server::new(stdin, stdout, socket).serve(service).await; } - -pub fn find_type_in_context(module: &mir::Module, token_idx: usize) -> Option { - for import in &module.imports { - if import.1.contains(token_idx) { - return Some(TypeKind::CustomType(mir::CustomTypeKey( - "d".to_owned(), - SourceModuleId(1), - ))); - } - } - for typedef in &module.typedefs { - if !typedef.meta.contains(token_idx) { - continue; - } - - match &typedef.kind { - mir::TypeDefinitionKind::Struct(StructType(fields)) => { - for field in fields { - if field.2.contains(token_idx) { - return Some(field.1.clone()); - } - } - } - } - } - - for binop in &module.binop_defs { - if let Some(meta) = binop.block_meta() { - if !meta.contains(token_idx) { - continue; - } - } else { - continue; - } - - return match &binop.fn_kind { - mir::FunctionDefinitionKind::Local(block, _) => find_type_in_block(&block, module.module_id, token_idx), - mir::FunctionDefinitionKind::Extern(_) => None, - mir::FunctionDefinitionKind::Intrinsic(_) => None, - }; - } - - for (_, function) in &module.associated_functions { - if !(function.signature() + function.block_meta()).contains(token_idx) { - continue; - } - - for param in &function.parameters { - if param.meta.contains(token_idx) { - return Some(param.ty.clone()); - } - } - - return match &function.kind { - mir::FunctionDefinitionKind::Local(block, _) => find_type_in_block(&block, module.module_id, token_idx), - mir::FunctionDefinitionKind::Extern(_) => None, - mir::FunctionDefinitionKind::Intrinsic(_) => None, - }; - } - - for function in &module.functions { - if !(function.signature() + function.block_meta()).contains(token_idx) { - continue; - } - - for param in &function.parameters { - if param.meta.contains(token_idx) { - return Some(param.ty.clone()); - } - } - - return match &function.kind { - mir::FunctionDefinitionKind::Local(block, _) => find_type_in_block(&block, module.module_id, token_idx), - mir::FunctionDefinitionKind::Extern(_) => None, - mir::FunctionDefinitionKind::Intrinsic(_) => None, - }; - } - None -} - -pub fn find_type_in_block(block: &mir::Block, module_id: SourceModuleId, token_idx: usize) -> Option { - if !block.meta.contains(token_idx) { - return Some(TypeKind::Bool); - } - - for statement in &block.statements { - if !statement.1.contains(token_idx) { - continue; - } - match &statement.0 { - mir::StmtKind::Let(named_variable_ref, _, expression) => { - if named_variable_ref.2.contains(token_idx) { - return expression - .return_type(&Default::default(), module_id) - .ok() - .map(|(_, ty)| ty); - } else { - return find_type_in_expr(&expression, module_id, token_idx); - } - } - mir::StmtKind::Set(lhs, rhs) => { - return find_type_in_expr(lhs, module_id, token_idx).or(find_type_in_expr(rhs, module_id, token_idx)); - } - mir::StmtKind::Import(_) => {} - mir::StmtKind::Expression(expression) => return find_type_in_expr(expression, module_id, token_idx), - mir::StmtKind::While(WhileStatement { condition, block, .. }) => { - return find_type_in_expr(condition, module_id, token_idx) - .or(find_type_in_block(block, module_id, token_idx)); - } - } - } - - if let Some((_, Some(return_exp))) = &block.return_expression { - if let Some(ty) = find_type_in_expr(return_exp, module_id, token_idx) { - return Some(ty); - } - } - - None -} - -pub fn find_type_in_expr(expr: &mir::Expression, module_id: SourceModuleId, token_idx: usize) -> Option { - if !expr.1.contains(token_idx) { - return None; - } - - match &expr.0 { - mir::ExprKind::Variable(named_variable_ref) => Some(named_variable_ref.0.clone()), - mir::ExprKind::Indexed(value, type_kind, index_expr) => Some( - find_type_in_expr(&value, module_id, token_idx) - .or(find_type_in_expr(&index_expr, module_id, token_idx)) - .unwrap_or(type_kind.clone()), - ), - mir::ExprKind::Accessed(expression, type_kind, _, meta) => { - if meta.contains(token_idx) { - Some(type_kind.clone()) - } else { - find_type_in_expr(&expression, module_id, token_idx) - } - } - mir::ExprKind::Array(expressions) => { - for expr in expressions { - if let Some(ty) = find_type_in_expr(expr, module_id, token_idx) { - return Some(ty); - } - } - None - } - mir::ExprKind::Struct(name, items) => { - for (_, expr, meta) in items { - if meta.contains(token_idx) { - return expr.return_type(&Default::default(), module_id).map(|(_, t)| t).ok(); - } - if let Some(ty) = find_type_in_expr(expr, module_id, token_idx) { - return Some(ty); - } - } - Some(TypeKind::CustomType(mir::CustomTypeKey(name.clone(), module_id))) - } - mir::ExprKind::Literal(literal) => Some(literal.as_type()), - mir::ExprKind::BinOp(_, lhs, rhs, type_kind) => { - if let Some(ty) = find_type_in_expr(lhs, module_id, token_idx) { - return Some(ty); - } - if let Some(ty) = find_type_in_expr(rhs, module_id, token_idx) { - return Some(ty); - } - Some(type_kind.clone()) - } - mir::ExprKind::FunctionCall(FunctionCall { - return_type, - parameters, - .. - }) => { - for expr in parameters { - if let Some(ty) = find_type_in_expr(expr, module_id, token_idx) { - return Some(ty); - } - } - Some(return_type.clone()) - } - mir::ExprKind::AssociatedFunctionCall( - _, - FunctionCall { - return_type, - parameters, - .. - }, - ) => { - for expr in parameters { - if let Some(ty) = find_type_in_expr(expr, module_id, token_idx) { - return Some(ty); - } - } - Some(return_type.clone()) - } - mir::ExprKind::If(IfExpression(cond, then_e, else_e)) => find_type_in_expr(&cond, module_id, token_idx) - .or(find_type_in_expr(&then_e, module_id, token_idx)) - .or(else_e.clone().and_then(|e| find_type_in_expr(&e, module_id, token_idx))), - mir::ExprKind::Block(block) => find_type_in_block(block, module_id, token_idx), - mir::ExprKind::Borrow(expression, mutable) => { - if let Some(ty) = find_type_in_expr(&expression, module_id, token_idx) { - return Some(ty); - } - if let Ok(inner) = expression.return_type(&Default::default(), module_id).map(|(_, ty)| ty) { - Some(TypeKind::Borrow(Box::new(inner.clone()), *mutable)) - } else { - None - } - } - mir::ExprKind::Deref(expression) => { - if let Some(ty) = find_type_in_expr(&expression, module_id, token_idx) { - return Some(ty); - } - if let Ok(TypeKind::Borrow(inner, _)) = - expression.return_type(&Default::default(), module_id).map(|(_, ty)| ty) - { - Some(*inner.clone()) - } else { - Some(TypeKind::CustomType(mir::CustomTypeKey( - "ä".to_owned(), - SourceModuleId(1), - ))) - } - } - mir::ExprKind::CastTo(expression, type_kind) => { - Some(find_type_in_expr(&expression, module_id, token_idx).unwrap_or(type_kind.clone())) - } - mir::ExprKind::GlobalRef(_, type_kind) => Some(type_kind.clone()), - } -} diff --git a/reid/src/ast/parse.rs b/reid/src/ast/parse.rs index 2a64150..d42d1cd 100644 --- a/reid/src/ast/parse.rs +++ b/reid/src/ast/parse.rs @@ -610,7 +610,7 @@ impl Parse for LetStatement { stream.expect(Token::Equals)?; let expression = stream.parse()?; - stream.expect(Token::Semi)?; + stream.expect_nonfatal(Token::Semi); Ok(LetStatement { name: variable, ty, @@ -643,7 +643,7 @@ impl Parse for ImportStatement { Err(stream.expected_err("identifier")?)? } - stream.expect(Token::Semi)?; + stream.expect_nonfatal(Token::Semi); Ok(ImportStatement(import_list, stream.get_range().unwrap())) } @@ -788,7 +788,7 @@ impl Parse for Block { // if semicolon is missing. if !matches!(e, Expression(ExpressionKind::IfExpr(_), _)) { // In theory could ignore the missing semicolon.. - return Err(stream.expected_err("semicolon to complete statement")?); + stream.expected_err_nonfatal("semicolon to complete statement"); } statements.push(BlockLevelStatement::Expression(e)); @@ -997,7 +997,7 @@ impl Parse for SetStatement { let var_ref = stream.parse()?; stream.expect(Token::Equals)?; let expr = stream.parse()?; - stream.expect(Token::Semi)?; + stream.expect_nonfatal(Token::Semi); Ok(SetStatement(var_ref, expr, stream.get_range().unwrap())) } } @@ -1038,7 +1038,7 @@ impl Parse for TopLevelStatement { stream.next(); // Consume Extern stream.expect(Token::FnKeyword)?; let extern_fn = Stmt::ExternFunction(stream.parse()?); - stream.expect(Token::Semi)?; + stream.expect_nonfatal(Token::Semi); extern_fn } Some(Token::FnKeyword) | Some(Token::PubKeyword) => Stmt::FunctionDefinition(stream.parse()?), diff --git a/reid/src/ast/token_stream.rs b/reid/src/ast/token_stream.rs index 829f08f..5a0249f 100644 --- a/reid/src/ast/token_stream.rs +++ b/reid/src/ast/token_stream.rs @@ -1,6 +1,8 @@ //! Contains relevant code for parsing tokens received from //! Lexing/Tokenizing-stage. +use std::{cell::RefCell, rc::Rc}; + use crate::{ ast::parse::Parse, lexer::{FullToken, Token}, @@ -12,6 +14,7 @@ use crate::{ pub struct TokenStream<'a, 'b> { ref_position: Option<&'b mut usize>, tokens: &'a [FullToken], + errors: Rc>>, pub position: usize, } @@ -20,6 +23,7 @@ impl<'a, 'b> TokenStream<'a, 'b> { TokenStream { ref_position: None, tokens, + errors: Rc::new(RefCell::new(Vec::new())), position: 0, } } @@ -38,6 +42,16 @@ impl<'a, 'b> TokenStream<'a, 'b> { )) } + /// Returns expected-error for the next token in-line. Useful in conjunction + /// with [`TokenStream::peek`] + pub fn expected_err_nonfatal>(&mut self, expected: T) { + let err = match self.expected_err(expected) { + Ok(e) => e, + Err(e) => e, + }; + self.errors.borrow_mut().push(err); + } + /// Returns expected-error for the previous token that was already consumed. /// Useful in conjunction with [`TokenStream::next`] pub fn expecting_err>(&mut self, expected: T) -> Result { @@ -50,6 +64,16 @@ impl<'a, 'b> TokenStream<'a, 'b> { )) } + /// Returns expected-error for the previous token that was already consumed. + /// Useful in conjunction with [`TokenStream::next`] + pub fn expecting_err_nonfatal>(&mut self, expected: T) { + let err = match self.expecting_err(expected) { + Ok(e) => e, + Err(e) => e, + }; + self.errors.borrow_mut().push(err); + } + pub fn expect(&mut self, token: Token) -> Result<(), Error> { if let (pos, Some(peeked)) = self.next_token(self.position) { if token == peeked.token { @@ -63,6 +87,18 @@ impl<'a, 'b> TokenStream<'a, 'b> { } } + pub fn expect_nonfatal(&mut self, token: Token) { + if let (pos, Some(peeked)) = self.next_token(self.position) { + if token == peeked.token { + self.position = pos + 1; + } else { + self.expecting_err_nonfatal(token); + } + } else { + self.expecting_err_nonfatal(token); + } + } + pub fn next(&mut self) -> Option { let (position, token) = self.next_token(self.position); self.position = position + 1; @@ -147,6 +183,7 @@ impl<'a, 'b> TokenStream<'a, 'b> { let clone = TokenStream { ref_position: Some(&mut ref_pos), tokens: self.tokens, + errors: self.errors.clone(), position, }; @@ -197,6 +234,10 @@ impl<'a, 'b> TokenStream<'a, 'b> { } (from, self.tokens.get(from)) } + + pub fn errors(&self) -> Vec { + self.errors.borrow().clone().clone() + } } impl Drop for TokenStream<'_, '_> { diff --git a/reid/src/codegen/mod.rs b/reid/src/codegen/mod.rs index f1ee527..15f16f6 100644 --- a/reid/src/codegen/mod.rs +++ b/reid/src/codegen/mod.rs @@ -1326,8 +1326,6 @@ impl mir::Expression { (val.1.clone(), type_kind.clone()) }; - dbg!(&ty, type_kind); - match (&ty, type_kind) { (TypeKind::UserPtr(_), TypeKind::UserPtr(_)) => Some(StackValue( val.0.derive( diff --git a/reid/src/lib.rs b/reid/src/lib.rs index f72bda2..00c4d6e 100644 --- a/reid/src/lib.rs +++ b/reid/src/lib.rs @@ -105,7 +105,7 @@ pub fn compile_module<'map>( map: &'map mut ErrorModules, path: Option, is_main: bool, -) -> Result { +) -> Result, ReidError> { let module = map.module(&module_id).cloned().unwrap(); let mut token_stream = TokenStream::from(&tokens); @@ -117,6 +117,8 @@ pub fn compile_module<'map>( statements.push(statement); } + let errors = token_stream.errors(); + drop(token_stream); let ast_module = ast::Module { @@ -127,11 +129,33 @@ pub fn compile_module<'map>( is_main, }; + if errors.len() > 0 { + return Ok(Err(( + ast_module, + ReidError::from_kind( + errors + .into_iter() + .map(|e| { + error_raporting::ErrorKind::from(mir::pass::Error { + metadata: mir::Metadata { + source_module_id: module_id, + range: *e.get_range().unwrap_or(&Default::default()), + position: None, + }, + kind: e, + }) + }) + .collect(), + map.clone(), + ), + ))); + } + #[cfg(debug_assertions)] #[cfg(feature = "log_output")] dbg!(&ast_module); - Ok(ast_module.process(module_id)) + Ok(Ok(ast_module.process(module_id))) } pub fn perform_all_passes<'map>( @@ -293,7 +317,7 @@ pub fn compile_and_pass<'map>( let name = path.file_name().unwrap().to_str().unwrap().to_owned(); let (id, tokens) = parse_module(source, name, module_map)?; - let module = compile_module(id, tokens, module_map, Some(path.clone()), true)?; + let module = compile_module(id, tokens, module_map, Some(path.clone()), true)?.map_err(|(_, e)| e)?; let mut mir_context = mir::Context::from(vec![module], path.parent().unwrap().to_owned()); diff --git a/reid/src/mir/linker.rs b/reid/src/mir/linker.rs index 1b4c992..a8c51f4 100644 --- a/reid/src/mir/linker.rs +++ b/reid/src/mir/linker.rs @@ -52,7 +52,7 @@ pub enum ErrorKind { pub fn compile_std(module_map: &mut ErrorModules) -> Result { let (id, tokens) = parse_module(STD_SOURCE, STD_NAME, module_map)?; - let module = compile_module(id, tokens, module_map, None, false)?; + let module = compile_module(id, tokens, module_map, None, false)?.map_err(|(_, e)| e)?; let module_id = module.module_id; let mut mir_context = super::Context::from(vec![module], Default::default()); @@ -156,21 +156,33 @@ impl<'map> Pass for LinkerPass<'map> { }; match compile_module(id, tokens, &mut self.module_map, Some(file_path), false) { - Ok(imported_module) => { - if imported_module.is_main { + Ok(res) => match res { + Ok(imported_module) => { + if imported_module.is_main { + state.ok::<_, Infallible>( + Err(ErrorKind::TriedLinkingMain(module_name.clone())), + import.1, + ); + continue; + } + let module_id = imported_module.module_id; + module_ids.insert(imported_module.name.clone(), imported_module.module_id); + modules.insert(module_id, Rc::new(RefCell::new(imported_module))); + let imported = modules.get_mut(&module_id).unwrap(); + modules_to_process.push(imported.clone()); + imported + } + Err((_, err)) => { state.ok::<_, Infallible>( - Err(ErrorKind::TriedLinkingMain(module_name.clone())), + Err(ErrorKind::ModuleCompilationError( + module_name.clone(), + format!("{}", err), + )), import.1, ); continue; } - let module_id = imported_module.module_id; - module_ids.insert(imported_module.name.clone(), imported_module.module_id); - modules.insert(module_id, Rc::new(RefCell::new(imported_module))); - let imported = modules.get_mut(&module_id).unwrap(); - modules_to_process.push(imported.clone()); - imported - } + }, Err(err) => { state.ok::<_, Infallible>( Err(ErrorKind::ModuleCompilationError( diff --git a/reid/tests/e2e.rs b/reid/tests/e2e.rs index 882bfa7..db30f45 100644 --- a/reid/tests/e2e.rs +++ b/reid/tests/e2e.rs @@ -16,7 +16,7 @@ fn test_compile(source: &str, name: &str) -> CompileOutput { let mut map = Default::default(); let (id, tokens) = assert_err(parse_module(source, name, &mut map)); - let module = assert_err(compile_module(id, tokens, &mut map, None, true)); + let module = assert_err(assert_err(compile_module(id, tokens, &mut map, None, true)).map_err(|(_, e)| e)); let mut mir_context = mir::Context::from(vec![module], Default::default()); assert_err(perform_all_passes(&mut mir_context, &mut map));