This commit is contained in:
Edgar 2023-06-11 12:35:18 +02:00
parent 8c212d948d
commit 071f5ce0f9
No known key found for this signature in database
GPG key ID: 70ADAE8F35904387
4 changed files with 150 additions and 46 deletions

View file

@ -3,6 +3,7 @@ use crate::{
codegen::ProgramData, codegen::ProgramData,
lexer::LexicalError, lexer::LexicalError,
tokens::Token, tokens::Token,
type_analysis::TypeError,
}; };
use annotate_snippets::{ use annotate_snippets::{
display_list::{DisplayList, FormatOptions}, display_list::{DisplayList, FormatOptions},
@ -97,7 +98,6 @@ pub fn print_error(source: &str, err: ParseError<usize, Token, LexicalError>) {
line_start: 1, line_start: 1,
fold: true, fold: true,
origin: None, origin: None,
annotations: vec![SourceAnnotation { annotations: vec![SourceAnnotation {
label: "invalid token", label: "invalid token",
annotation_type: AnnotationType::Error, annotation_type: AnnotationType::Error,
@ -126,14 +126,14 @@ pub fn print_error(source: &str, err: ParseError<usize, Token, LexicalError>) {
}), }),
footer: vec![], footer: vec![],
slices: vec![Slice { slices: vec![Slice {
source: dbg!(source), source: source,
line_start: 1, line_start: 1,
fold: false, fold: false,
origin: None, origin: None,
annotations: vec![SourceAnnotation { annotations: vec![SourceAnnotation {
label: "invalid token (lexical error)", label: "invalid token (lexical error)",
annotation_type: AnnotationType::Error, annotation_type: AnnotationType::Error,
range: dbg!((range.start, range.end)), range: (range.start, range.end),
}], }],
}], }],
opt: FormatOptions { opt: FormatOptions {
@ -147,3 +147,67 @@ pub fn print_error(source: &str, err: ParseError<usize, Token, LexicalError>) {
}, },
}; };
} }
pub fn print_type_error(source: &str, err: TypeError) {
dbg!(&err);
match err {
TypeError::Mismatch {
found,
expected,
span,
} => {
let snippet = Snippet {
title: Some(Annotation {
id: None,
label: Some("type mismatch"),
annotation_type: AnnotationType::Error,
}),
footer: vec![],
slices: vec![Slice {
source,
line_start: 1,
fold: false,
origin: None,
annotations: vec![SourceAnnotation {
label: "type mismatch",
annotation_type: AnnotationType::Error,
range: span,
}],
}],
opt: FormatOptions {
color: true,
..Default::default()
},
};
let dl = DisplayList::from(snippet);
println!("{dl}");
}
TypeError::UndeclaredVariable { name, span } => {
let snippet = Snippet {
title: Some(Annotation {
id: None,
label: Some("undeclared variable"),
annotation_type: AnnotationType::Error,
}),
footer: vec![],
slices: vec![Slice {
source,
line_start: 1,
fold: false,
origin: None,
annotations: vec![SourceAnnotation {
label: "undeclared variable",
annotation_type: AnnotationType::Error,
range: span,
}],
}],
opt: FormatOptions {
color: true,
..Default::default()
},
};
let dl = DisplayList::from(snippet);
println!("{dl}");
}
};
}

View file

@ -178,12 +178,12 @@ impl<'ctx> CodeGen<'ctx> {
let args_types: Vec<BasicMetadataTypeEnum<'ctx>> = let args_types: Vec<BasicMetadataTypeEnum<'ctx>> =
args_types.into_iter().map(|t| t.into()).collect_vec(); args_types.into_iter().map(|t| t.into()).collect_vec();
let (fn_type, ret_type) = match &function.return_type { let fn_type = match &function.return_type {
Some(id) => { Some(id) => {
let return_type = self.get_llvm_type(id)?; let return_type = self.get_llvm_type(id)?;
(return_type.fn_type(&args_types, false), Some(id.clone())) return_type.fn_type(&args_types, false)
} }
None => (self.context.void_type().fn_type(&args_types, false), None), None => self.context.void_type().fn_type(&args_types, false),
}; };
self.module.add_function(&function.name, fn_type, None); self.module.add_function(&function.name, fn_type, None);
@ -447,6 +447,8 @@ impl<'ctx> CodeGen<'ctx> {
.compile_expression(rhs, variables, scope_info)? .compile_expression(rhs, variables, scope_info)?
.expect("should have result"); .expect("should have result");
assert_eq!(lhs.get_type(), rhs.get_type(), "type mismatch");
let lhs = lhs.into_int_value(); let lhs = lhs.into_int_value();
let rhs = rhs.into_int_value(); let rhs = rhs.into_int_value();

View file

@ -3,12 +3,15 @@
use check::print_error; use check::print_error;
use clap::{Parser, Subcommand}; use clap::{Parser, Subcommand};
use codegen::ProgramData; use codegen::ProgramData;
use color_eyre::Result;
use inkwell::{context::Context, execution_engine::JitFunction, OptimizationLevel}; use inkwell::{context::Context, execution_engine::JitFunction, OptimizationLevel};
use lalrpop_util::lalrpop_mod; use lalrpop_util::lalrpop_mod;
use std::{fs, path::PathBuf, println}; use std::{fs, path::PathBuf, println};
use crate::{ast::Program, check::Check, lexer::Lexer}; use crate::{
ast::Program,
check::{print_type_error, Check},
lexer::Lexer,
};
pub mod ast; pub mod ast;
pub mod check; pub mod check;
@ -91,7 +94,7 @@ fn check_program(program: &ProgramData, ast: &ast::Program) -> bool {
error_count == 0 error_count == 0
} }
fn main() -> Result<()> { fn main() -> color_eyre::Result<()> {
color_eyre::install()?; color_eyre::install()?;
tracing_subscriber::fmt::init(); tracing_subscriber::fmt::init();
let args = Args::parse(); let args = Args::parse();
@ -102,7 +105,10 @@ fn main() -> Result<()> {
let lexer = Lexer::new(code.as_str()); let lexer = Lexer::new(code.as_str());
let parser = grammar::ProgramParser::new(); let parser = grammar::ProgramParser::new();
let mut ast = parser.parse(lexer)?; let mut ast = parser.parse(lexer)?;
type_analysis::type_inference(&mut ast); if let Err(e) = type_analysis::type_check(&mut ast) {
print_type_error(&code, e);
return Ok(());
}
let program = ProgramData::new(&input, &code); let program = ProgramData::new(&input, &code);
check_program(&program, &ast); check_program(&program, &ast);
} }
@ -112,7 +118,10 @@ fn main() -> Result<()> {
let parser = grammar::ProgramParser::new(); let parser = grammar::ProgramParser::new();
match parser.parse(lexer) { match parser.parse(lexer) {
Ok(mut ast) => { Ok(mut ast) => {
type_analysis::type_inference(&mut ast); if let Err(e) = type_analysis::type_check(&mut ast) {
print_type_error(&code, e);
return Ok(());
}
println!("{ast:#?}"); println!("{ast:#?}");
} }
Err(e) => { Err(e) => {
@ -130,7 +139,10 @@ fn main() -> Result<()> {
let lexer = Lexer::new(code.as_str()); let lexer = Lexer::new(code.as_str());
let parser = grammar::ProgramParser::new(); let parser = grammar::ProgramParser::new();
let mut ast: Program = parser.parse(lexer)?; let mut ast: Program = parser.parse(lexer)?;
type_analysis::type_inference(&mut ast); if let Err(e) = type_analysis::type_check(&mut ast) {
print_type_error(&code, e);
return Ok(());
}
let program = ProgramData::new(&input, &code); let program = ProgramData::new(&input, &code);
@ -156,7 +168,11 @@ fn main() -> Result<()> {
let code = fs::read_to_string(&input)?; let code = fs::read_to_string(&input)?;
let lexer = Lexer::new(&code[..]); let lexer = Lexer::new(&code[..]);
let parser = grammar::ProgramParser::new(); let parser = grammar::ProgramParser::new();
let ast = parser.parse(lexer).unwrap(); let mut ast: Program = parser.parse(lexer)?;
if let Err(e) = type_analysis::type_check(&mut ast) {
print_type_error(&code, e);
return Ok(());
}
let program = ProgramData::new(&input, &code); let program = ProgramData::new(&input, &code);

View file

@ -2,21 +2,30 @@ use std::collections::{HashMap, HashSet};
use crate::ast::{self, Expression, Function, Statement, TypeExp}; use crate::ast::{self, Expression, Function, Statement, TypeExp};
#[derive(Debug, Clone)]
pub enum TypeError {
Mismatch {
found: TypeExp,
expected: TypeExp,
span: (usize, usize),
},
UndeclaredVariable {
name: String,
span: (usize, usize),
},
}
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
struct Storage { struct Storage {
structs: HashMap<String, HashMap<String, TypeExp>>, structs: HashMap<String, HashMap<String, TypeExp>>,
functions: HashMap<String, Function>, functions: HashMap<String, Function>,
} }
// problem with scopes,
// let x = 2;
// let x = 2i64;
type ScopeMap = HashMap<String, Vec<Option<TypeExp>>>; type ScopeMap = HashMap<String, Vec<Option<TypeExp>>>;
// this works, but need to find a way to store the found info + handle literal integer types (or not?) // this works, but need to find a way to store the found info + handle literal integer types (or not?)
// maybe use scope ids // maybe use scope ids
pub fn type_inference(ast: &mut ast::Program) { pub fn type_check(ast: &mut ast::Program) -> Result<(), TypeError> {
let mut storage = Storage::default(); let mut storage = Storage::default();
// gather global constructs first // gather global constructs first
@ -40,8 +49,6 @@ pub fn type_inference(ast: &mut ast::Program) {
} }
} }
dbg!(&storage);
for statement in ast.statements.iter_mut() { for statement in ast.statements.iter_mut() {
if let Statement::Function(function) = statement { if let Statement::Function(function) = statement {
let mut scope_vars: ScopeMap = HashMap::new(); let mut scope_vars: ScopeMap = HashMap::new();
@ -52,7 +59,7 @@ pub fn type_inference(ast: &mut ast::Program) {
let func_info = function.clone(); let func_info = function.clone();
let (new_scope_vars, _) = let (new_scope_vars, _) =
type_inference_scope(&mut function.body, &scope_vars, &func_info, &storage); type_inference_scope(&mut function.body, &scope_vars, &func_info, &storage)?;
// todo: check all vars have type info? // todo: check all vars have type info?
function.scope_type_info = new_scope_vars function.scope_type_info = new_scope_vars
.into_iter() .into_iter()
@ -60,6 +67,7 @@ pub fn type_inference(ast: &mut ast::Program) {
.collect(); .collect();
} }
} }
Ok(())
} }
/// Finds variable types in the scope, returns newly created variables to handle shadowing /// Finds variable types in the scope, returns newly created variables to handle shadowing
@ -68,7 +76,7 @@ fn type_inference_scope(
scope_vars: &ScopeMap, scope_vars: &ScopeMap,
func: &Function, func: &Function,
storage: &Storage, storage: &Storage,
) -> (ScopeMap, HashSet<String>) { ) -> Result<(ScopeMap, HashSet<String>), TypeError> {
let mut scope_vars = scope_vars.clone(); let mut scope_vars = scope_vars.clone();
let mut new_vars: HashSet<String> = HashSet::new(); let mut new_vars: HashSet<String> = HashSet::new();
@ -78,11 +86,11 @@ fn type_inference_scope(
name, name,
value, value,
value_type, value_type,
span: _, span,
} => { } => {
new_vars.insert(name.clone()); new_vars.insert(name.clone());
let exp_type = type_inference_expression(value, &mut scope_vars, storage, None); let exp_type = type_inference_expression(value, &mut scope_vars, storage, None)?;
if !scope_vars.contains_key(name) { if !scope_vars.contains_key(name) {
scope_vars.insert(name.clone(), vec![]); scope_vars.insert(name.clone(), vec![]);
@ -94,27 +102,31 @@ fn type_inference_scope(
var.push(exp_type); var.push(exp_type);
} else { } else {
if exp_type.is_some() && &exp_type != value_type { if exp_type.is_some() && &exp_type != value_type {
panic!("let type mismatch: {:?} != {:?}", value_type, exp_type); Err(TypeError::Mismatch {
found: exp_type.clone().unwrap(),
expected: value_type.clone().unwrap(),
span: *span,
})?;
} }
var.push(value_type.clone()); var.push(value_type.clone());
} }
} }
Statement::Mutate { Statement::Mutate { name, value, span } => {
name,
value,
span: _,
} => {
if !scope_vars.contains_key(name) { if !scope_vars.contains_key(name) {
panic!("undeclared variable"); panic!("undeclared variable");
} }
let exp_type = type_inference_expression(value, &mut scope_vars, storage, None); let exp_type = type_inference_expression(value, &mut scope_vars, storage, None)?;
let var = scope_vars.get_mut(name).unwrap().last_mut().unwrap(); let var = scope_vars.get_mut(name).unwrap().last_mut().unwrap();
if var.is_none() { if var.is_none() {
*var = exp_type; *var = exp_type;
} else if exp_type.is_some() && &exp_type != var { } else if exp_type.is_some() && &exp_type != var {
panic!("mutate type mismatch: {:?} != {:?}", var, exp_type); Err(TypeError::Mismatch {
found: exp_type.clone().unwrap(),
expected: var.clone().unwrap(),
span: *span,
})?;
} }
} }
Statement::If { Statement::If {
@ -129,10 +141,10 @@ fn type_inference_scope(
&mut scope_vars, &mut scope_vars,
storage, storage,
Some(TypeExp::Boolean), Some(TypeExp::Boolean),
); )?;
let (new_scope_vars, new_vars) = let (new_scope_vars, new_vars) =
type_inference_scope(body, &scope_vars, func, storage); type_inference_scope(body, &scope_vars, func, storage)?;
for (k, v) in new_scope_vars.iter() { for (k, v) in new_scope_vars.iter() {
// not a new var within the scope (shadowing), so type info is valid // not a new var within the scope (shadowing), so type info is valid
@ -148,7 +160,7 @@ fn type_inference_scope(
if let Some(body) = else_body { if let Some(body) = else_body {
let (new_scope_vars, new_vars) = let (new_scope_vars, new_vars) =
type_inference_scope(body, &scope_vars, func, storage); type_inference_scope(body, &scope_vars, func, storage)?;
for (k, v) in new_scope_vars.iter() { for (k, v) in new_scope_vars.iter() {
// not a new var within the scope (shadowing), so type info is valid // not a new var within the scope (shadowing), so type info is valid
@ -170,7 +182,7 @@ fn type_inference_scope(
&mut scope_vars, &mut scope_vars,
storage, storage,
func.return_type.clone(), func.return_type.clone(),
); )?;
} }
} }
Statement::Function(_) => unreachable!(), Statement::Function(_) => unreachable!(),
@ -178,7 +190,7 @@ fn type_inference_scope(
} }
} }
(scope_vars, new_vars) Ok((scope_vars, new_vars))
} }
fn type_inference_expression( fn type_inference_expression(
@ -186,8 +198,8 @@ fn type_inference_expression(
scope_vars: &mut ScopeMap, scope_vars: &mut ScopeMap,
storage: &Storage, storage: &Storage,
expected_type: Option<TypeExp>, expected_type: Option<TypeExp>,
) -> Option<TypeExp> { ) -> Result<Option<TypeExp>, TypeError> {
match exp { Ok(match exp {
Expression::Literal(lit) => { Expression::Literal(lit) => {
match lit { match lit {
ast::LiteralValue::String(_) => None, // todo ast::LiteralValue::String(_) => None, // todo
@ -214,7 +226,13 @@ fn type_inference_expression(
*var = expected_type.clone(); *var = expected_type.clone();
expected_type expected_type
} else if expected_type.is_some() { } else if expected_type.is_some() {
assert_eq!(*var, expected_type, "type mismatch with variables"); if *var != expected_type {
Err(TypeError::Mismatch {
found: expected_type.clone().unwrap(),
expected: var.clone().unwrap(),
span: name.span,
})?;
}
expected_type expected_type
} else { } else {
var.clone() var.clone()
@ -229,7 +247,7 @@ fn type_inference_expression(
for (i, arg) in args.iter().enumerate() { for (i, arg) in args.iter().enumerate() {
let arg_type = func.params[i].type_exp.clone(); let arg_type = func.params[i].type_exp.clone();
// result is ignored, but need these to infer call arg types // result is ignored, but need these to infer call arg types
type_inference_expression(arg, scope_vars, storage, Some(arg_type)); type_inference_expression(arg, scope_vars, storage, Some(arg_type))?;
} }
func.return_type func.return_type
@ -238,15 +256,19 @@ fn type_inference_expression(
ast::OpCode::Eq | ast::OpCode::Ne => Some(TypeExp::Boolean), ast::OpCode::Eq | ast::OpCode::Ne => Some(TypeExp::Boolean),
_ => { _ => {
let lhs_type = let lhs_type =
type_inference_expression(lhs, scope_vars, storage, expected_type.clone()); type_inference_expression(lhs, scope_vars, storage, expected_type.clone())?;
let rhs_type = type_inference_expression(rhs, scope_vars, storage, expected_type); let rhs_type = type_inference_expression(rhs, scope_vars, storage, expected_type)?;
if lhs_type.is_some() && rhs_type.is_some() { if lhs_type.is_some() && rhs_type.is_some() && lhs_type != rhs_type {
assert_eq!(lhs_type, rhs_type, "types should match"); Err(TypeError::Mismatch {
found: rhs_type.clone().unwrap(),
expected: lhs_type.clone().unwrap(),
span: (0, 0), // todo
})?;
} }
lhs_type.or(rhs_type) lhs_type.or(rhs_type)
} }
}, },
} })
} }