Make LSP use a more general analysis structure

This commit is contained in:
Sofia 2025-08-02 21:11:33 +03:00
parent dae39bc9d2
commit 8595da0c30
8 changed files with 456 additions and 338 deletions

294
reid-lsp/src/analysis.rs Normal file
View File

@ -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<FullToken>,
pub token_analysis: HashMap<usize, SemanticAnalysis>,
pub error: Option<ReidError>,
}
#[derive(Debug, Clone)]
pub struct SemanticAnalysis {
pub ty: Option<TypeKind>,
}
pub fn analyze(
module_id: SourceModuleId,
tokens: Vec<FullToken>,
path: PathBuf,
map: &mut ErrorModules,
) -> Result<Option<StaticAnalysis>, 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<TypeKind> {
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<TypeKind> {
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<TypeKind> {
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()),
}
}

View File

@ -1,29 +1,28 @@
use std::collections::HashMap;
use std::path::PathBuf; use std::path::PathBuf;
use dashmap::DashMap; use dashmap::DashMap;
use reid::ast::lexer::{FullToken, Position}; use reid::ast::lexer::{FullToken, Position};
use reid::error_raporting::{ErrorModules, ReidError}; use reid::error_raporting::{self, ErrorModules, ReidError};
use reid::mir::{ use reid::mir::{SourceModuleId, TypeKind};
self, Context, FunctionCall, FunctionDefinition, FunctionParam, IfExpression, SourceModuleId, StructType, TypeKind, use reid::parse_module;
WhileStatement,
};
use reid::{compile_module, parse_module, perform_all_passes};
use tower_lsp::lsp_types::{ use tower_lsp::lsp_types::{
self, CompletionItem, CompletionOptions, CompletionParams, CompletionResponse, Diagnostic, DiagnosticSeverity, self, CompletionItem, CompletionOptions, CompletionParams, CompletionResponse, Diagnostic, DiagnosticSeverity,
DidChangeTextDocumentParams, DidOpenTextDocumentParams, Hover, HoverContents, HoverParams, HoverProviderCapability, DidChangeTextDocumentParams, DidOpenTextDocumentParams, Hover, HoverContents, HoverParams, HoverProviderCapability,
InitializeParams, InitializeResult, InitializedParams, MarkedString, MarkupContent, MarkupKind, MessageType, OneOf, InitializeParams, InitializeResult, InitializedParams, MarkupContent, MarkupKind, MessageType, OneOf, Range,
Range, ServerCapabilities, TextDocumentItem, TextDocumentSyncCapability, TextDocumentSyncKind, ServerCapabilities, TextDocumentItem, TextDocumentSyncCapability, TextDocumentSyncKind, TextDocumentSyncOptions,
TextDocumentSyncOptions, WorkspaceFoldersServerCapabilities, WorkspaceServerCapabilities, WorkspaceFoldersServerCapabilities, WorkspaceServerCapabilities,
}; };
use tower_lsp::{Client, LanguageServer, LspService, Server, jsonrpc}; use tower_lsp::{Client, LanguageServer, LspService, Server, jsonrpc};
use crate::analysis::{StaticAnalysis, analyze};
mod analysis;
#[derive(Debug)] #[derive(Debug)]
struct Backend { struct Backend {
client: Client, client: Client,
tokens: DashMap<String, Vec<FullToken>>, analysis: DashMap<String, StaticAnalysis>,
ast: DashMap<String, reid::ast::Module>, ast: DashMap<String, reid::ast::Module>,
types: DashMap<String, DashMap<FullToken, Option<TypeKind>>>,
} }
#[tower_lsp::async_trait] #[tower_lsp::async_trait]
@ -78,11 +77,11 @@ impl LanguageServer for Backend {
async fn hover(&self, params: HoverParams) -> jsonrpc::Result<Option<Hover>> { async fn hover(&self, params: HoverParams) -> jsonrpc::Result<Option<Hover>> {
let path = PathBuf::from(params.text_document_position_params.text_document.uri.path()); 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 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 position = params.text_document_position_params.position;
let token = if let Some(tokens) = &tokens { let token = if let Some(analysis) = &analysis {
tokens.iter().find(|tok| { analysis.tokens.iter().enumerate().find(|(_, tok)| {
tok.position.1 == position.line + 1 tok.position.1 == position.line + 1
&& (tok.position.0 <= position.character + 1 && (tok.position.0 <= position.character + 1
&& (tok.position.0 + tok.token.len() as u32) > position.character + 1) && (tok.position.0 + tok.token.len() as u32) > position.character + 1)
@ -91,8 +90,8 @@ impl LanguageServer for Backend {
None None
}; };
let (range, ty) = if let Some(token) = token { let (range, ty) = if let Some((idx, token)) = token {
if let Some(possible_ty) = self.types.get(&file_name).unwrap().get(token) { if let Some(possible_ty) = self.analysis.get(&file_name).unwrap().token_analysis.get(&idx) {
let start = token.position; let start = token.position;
let end = token.position.add(token.token.len() as u32); let end = token.position.add(token.token.len() as u32);
let range = Range { let range = Range {
@ -105,7 +104,7 @@ impl LanguageServer for Backend {
character: (end.0 as i32 - 1).max(0) as u32, 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)) (Some(range), format!("{}", ty))
} else { } else {
(Some(range), String::from("None type")) (Some(range), String::from("None type"))
@ -154,50 +153,33 @@ impl Backend {
let mut map = Default::default(); let mut map = Default::default();
let parse_res = parse(&params.text, path.clone(), &mut map); let parse_res = parse(&params.text, path.clone(), &mut map);
let (tokens, result) = match parse_res { 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)), Err(e) => (Vec::new(), Err(e)),
}; };
let mut diagnostics = Vec::new(); let mut diagnostics = Vec::new();
match result { match result {
Ok(Some(result)) => { Ok(Some(mut analysis)) => {
self.tokens.insert(file_name.clone(), result.tokens); if let Some(reid_error) = &mut analysis.error {
self.types.insert(file_name.clone(), result.types); 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(_) => {} Ok(_) => {}
Err(mut reid_error) => { Err(mut reid_error) => {
reid_error.errors.dedup(); reid_error.errors.dedup();
for error in reid_error.errors { for error in &reid_error.errors {
let meta = error.get_meta(); diagnostics.push(reid_error_into_diagnostic(error, &tokens));
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,
});
self.client.log_message(MessageType::INFO, format!("{}", error)).await; 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<FullToken>) -> 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 { struct CompileResult {
tokens: Vec<FullToken>, tokens: Vec<FullToken>,
types: DashMap<FullToken, Option<TypeKind>>, types: DashMap<FullToken, Option<TypeKind>>,
@ -220,36 +231,6 @@ fn parse(source: &str, path: PathBuf, map: &mut ErrorModules) -> Result<(SourceM
Ok(parse_module(source, file_name.clone(), map)?) Ok(parse_module(source, file_name.clone(), map)?)
} }
fn compile(
module_id: SourceModuleId,
tokens: Vec<FullToken>,
path: PathBuf,
map: &mut ErrorModules,
) -> Result<Option<CompileResult>, 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] #[tokio::main]
async fn main() { async fn main() {
let stdin = tokio::io::stdin(); let stdin = tokio::io::stdin();
@ -258,239 +239,7 @@ async fn main() {
let (service, socket) = LspService::new(|client| Backend { let (service, socket) = LspService::new(|client| Backend {
client, client,
ast: DashMap::new(), ast: DashMap::new(),
tokens: DashMap::new(), analysis: DashMap::new(),
types: DashMap::new(),
}); });
Server::new(stdin, stdout, socket).serve(service).await; Server::new(stdin, stdout, socket).serve(service).await;
} }
pub fn find_type_in_context(module: &mir::Module, token_idx: usize) -> Option<TypeKind> {
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<TypeKind> {
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<TypeKind> {
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()),
}
}

View File

@ -610,7 +610,7 @@ impl Parse for LetStatement {
stream.expect(Token::Equals)?; stream.expect(Token::Equals)?;
let expression = stream.parse()?; let expression = stream.parse()?;
stream.expect(Token::Semi)?; stream.expect_nonfatal(Token::Semi);
Ok(LetStatement { Ok(LetStatement {
name: variable, name: variable,
ty, ty,
@ -643,7 +643,7 @@ impl Parse for ImportStatement {
Err(stream.expected_err("identifier")?)? Err(stream.expected_err("identifier")?)?
} }
stream.expect(Token::Semi)?; stream.expect_nonfatal(Token::Semi);
Ok(ImportStatement(import_list, stream.get_range().unwrap())) Ok(ImportStatement(import_list, stream.get_range().unwrap()))
} }
@ -788,7 +788,7 @@ impl Parse for Block {
// if semicolon is missing. // if semicolon is missing.
if !matches!(e, Expression(ExpressionKind::IfExpr(_), _)) { if !matches!(e, Expression(ExpressionKind::IfExpr(_), _)) {
// In theory could ignore the missing semicolon.. // 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)); statements.push(BlockLevelStatement::Expression(e));
@ -997,7 +997,7 @@ impl Parse for SetStatement {
let var_ref = stream.parse()?; let var_ref = stream.parse()?;
stream.expect(Token::Equals)?; stream.expect(Token::Equals)?;
let expr = stream.parse()?; let expr = stream.parse()?;
stream.expect(Token::Semi)?; stream.expect_nonfatal(Token::Semi);
Ok(SetStatement(var_ref, expr, stream.get_range().unwrap())) Ok(SetStatement(var_ref, expr, stream.get_range().unwrap()))
} }
} }
@ -1038,7 +1038,7 @@ impl Parse for TopLevelStatement {
stream.next(); // Consume Extern stream.next(); // Consume Extern
stream.expect(Token::FnKeyword)?; stream.expect(Token::FnKeyword)?;
let extern_fn = Stmt::ExternFunction(stream.parse()?); let extern_fn = Stmt::ExternFunction(stream.parse()?);
stream.expect(Token::Semi)?; stream.expect_nonfatal(Token::Semi);
extern_fn extern_fn
} }
Some(Token::FnKeyword) | Some(Token::PubKeyword) => Stmt::FunctionDefinition(stream.parse()?), Some(Token::FnKeyword) | Some(Token::PubKeyword) => Stmt::FunctionDefinition(stream.parse()?),

View File

@ -1,6 +1,8 @@
//! Contains relevant code for parsing tokens received from //! Contains relevant code for parsing tokens received from
//! Lexing/Tokenizing-stage. //! Lexing/Tokenizing-stage.
use std::{cell::RefCell, rc::Rc};
use crate::{ use crate::{
ast::parse::Parse, ast::parse::Parse,
lexer::{FullToken, Token}, lexer::{FullToken, Token},
@ -12,6 +14,7 @@ use crate::{
pub struct TokenStream<'a, 'b> { pub struct TokenStream<'a, 'b> {
ref_position: Option<&'b mut usize>, ref_position: Option<&'b mut usize>,
tokens: &'a [FullToken], tokens: &'a [FullToken],
errors: Rc<RefCell<Vec<Error>>>,
pub position: usize, pub position: usize,
} }
@ -20,6 +23,7 @@ impl<'a, 'b> TokenStream<'a, 'b> {
TokenStream { TokenStream {
ref_position: None, ref_position: None,
tokens, tokens,
errors: Rc::new(RefCell::new(Vec::new())),
position: 0, 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<T: Into<String>>(&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. /// Returns expected-error for the previous token that was already consumed.
/// Useful in conjunction with [`TokenStream::next`] /// Useful in conjunction with [`TokenStream::next`]
pub fn expecting_err<T: Into<String>>(&mut self, expected: T) -> Result<Error, Error> { pub fn expecting_err<T: Into<String>>(&mut self, expected: T) -> Result<Error, Error> {
@ -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<T: Into<String>>(&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> { pub fn expect(&mut self, token: Token) -> Result<(), Error> {
if let (pos, Some(peeked)) = self.next_token(self.position) { if let (pos, Some(peeked)) = self.next_token(self.position) {
if token == peeked.token { 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<Token> { pub fn next(&mut self) -> Option<Token> {
let (position, token) = self.next_token(self.position); let (position, token) = self.next_token(self.position);
self.position = position + 1; self.position = position + 1;
@ -147,6 +183,7 @@ impl<'a, 'b> TokenStream<'a, 'b> {
let clone = TokenStream { let clone = TokenStream {
ref_position: Some(&mut ref_pos), ref_position: Some(&mut ref_pos),
tokens: self.tokens, tokens: self.tokens,
errors: self.errors.clone(),
position, position,
}; };
@ -197,6 +234,10 @@ impl<'a, 'b> TokenStream<'a, 'b> {
} }
(from, self.tokens.get(from)) (from, self.tokens.get(from))
} }
pub fn errors(&self) -> Vec<Error> {
self.errors.borrow().clone().clone()
}
} }
impl Drop for TokenStream<'_, '_> { impl Drop for TokenStream<'_, '_> {

View File

@ -1326,8 +1326,6 @@ impl mir::Expression {
(val.1.clone(), type_kind.clone()) (val.1.clone(), type_kind.clone())
}; };
dbg!(&ty, type_kind);
match (&ty, type_kind) { match (&ty, type_kind) {
(TypeKind::UserPtr(_), TypeKind::UserPtr(_)) => Some(StackValue( (TypeKind::UserPtr(_), TypeKind::UserPtr(_)) => Some(StackValue(
val.0.derive( val.0.derive(

View File

@ -105,7 +105,7 @@ pub fn compile_module<'map>(
map: &'map mut ErrorModules, map: &'map mut ErrorModules,
path: Option<PathBuf>, path: Option<PathBuf>,
is_main: bool, is_main: bool,
) -> Result<mir::Module, ReidError> { ) -> Result<Result<mir::Module, (ast::Module, ReidError)>, ReidError> {
let module = map.module(&module_id).cloned().unwrap(); let module = map.module(&module_id).cloned().unwrap();
let mut token_stream = TokenStream::from(&tokens); let mut token_stream = TokenStream::from(&tokens);
@ -117,6 +117,8 @@ pub fn compile_module<'map>(
statements.push(statement); statements.push(statement);
} }
let errors = token_stream.errors();
drop(token_stream); drop(token_stream);
let ast_module = ast::Module { let ast_module = ast::Module {
@ -127,11 +129,33 @@ pub fn compile_module<'map>(
is_main, 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(debug_assertions)]
#[cfg(feature = "log_output")] #[cfg(feature = "log_output")]
dbg!(&ast_module); dbg!(&ast_module);
Ok(ast_module.process(module_id)) Ok(Ok(ast_module.process(module_id)))
} }
pub fn perform_all_passes<'map>( 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 name = path.file_name().unwrap().to_str().unwrap().to_owned();
let (id, tokens) = parse_module(source, name, module_map)?; 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()); let mut mir_context = mir::Context::from(vec![module], path.parent().unwrap().to_owned());

View File

@ -52,7 +52,7 @@ pub enum ErrorKind {
pub fn compile_std(module_map: &mut ErrorModules) -> Result<Module, ReidError> { pub fn compile_std(module_map: &mut ErrorModules) -> Result<Module, ReidError> {
let (id, tokens) = parse_module(STD_SOURCE, STD_NAME, module_map)?; 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 module_id = module.module_id;
let mut mir_context = super::Context::from(vec![module], Default::default()); 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) { match compile_module(id, tokens, &mut self.module_map, Some(file_path), false) {
Ok(imported_module) => { Ok(res) => match res {
if imported_module.is_main { 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>( state.ok::<_, Infallible>(
Err(ErrorKind::TriedLinkingMain(module_name.clone())), Err(ErrorKind::ModuleCompilationError(
module_name.clone(),
format!("{}", err),
)),
import.1, import.1,
); );
continue; 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) => { Err(err) => {
state.ok::<_, Infallible>( state.ok::<_, Infallible>(
Err(ErrorKind::ModuleCompilationError( Err(ErrorKind::ModuleCompilationError(

View File

@ -16,7 +16,7 @@ fn test_compile(source: &str, name: &str) -> CompileOutput {
let mut map = Default::default(); let mut map = Default::default();
let (id, tokens) = assert_err(parse_module(source, name, &mut map)); 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()); let mut mir_context = mir::Context::from(vec![module], Default::default());
assert_err(perform_all_passes(&mut mir_context, &mut map)); assert_err(perform_all_passes(&mut mir_context, &mut map));