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,
lexer::LexicalError,
tokens::Token,
type_analysis::TypeError,
};
use annotate_snippets::{
display_list::{DisplayList, FormatOptions},
@ -97,7 +98,6 @@ pub fn print_error(source: &str, err: ParseError<usize, Token, LexicalError>) {
line_start: 1,
fold: true,
origin: None,
annotations: vec![SourceAnnotation {
label: "invalid token",
annotation_type: AnnotationType::Error,
@ -126,14 +126,14 @@ pub fn print_error(source: &str, err: ParseError<usize, Token, LexicalError>) {
}),
footer: vec![],
slices: vec![Slice {
source: dbg!(source),
source: source,
line_start: 1,
fold: false,
origin: None,
annotations: vec![SourceAnnotation {
label: "invalid token (lexical error)",
annotation_type: AnnotationType::Error,
range: dbg!((range.start, range.end)),
range: (range.start, range.end),
}],
}],
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>> =
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) => {
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);
@ -447,6 +447,8 @@ impl<'ctx> CodeGen<'ctx> {
.compile_expression(rhs, variables, scope_info)?
.expect("should have result");
assert_eq!(lhs.get_type(), rhs.get_type(), "type mismatch");
let lhs = lhs.into_int_value();
let rhs = rhs.into_int_value();

View file

@ -3,12 +3,15 @@
use check::print_error;
use clap::{Parser, Subcommand};
use codegen::ProgramData;
use color_eyre::Result;
use inkwell::{context::Context, execution_engine::JitFunction, OptimizationLevel};
use lalrpop_util::lalrpop_mod;
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 check;
@ -91,7 +94,7 @@ fn check_program(program: &ProgramData, ast: &ast::Program) -> bool {
error_count == 0
}
fn main() -> Result<()> {
fn main() -> color_eyre::Result<()> {
color_eyre::install()?;
tracing_subscriber::fmt::init();
let args = Args::parse();
@ -102,7 +105,10 @@ fn main() -> Result<()> {
let lexer = Lexer::new(code.as_str());
let parser = grammar::ProgramParser::new();
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);
check_program(&program, &ast);
}
@ -112,7 +118,10 @@ fn main() -> Result<()> {
let parser = grammar::ProgramParser::new();
match parser.parse(lexer) {
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:#?}");
}
Err(e) => {
@ -130,7 +139,10 @@ fn main() -> Result<()> {
let lexer = Lexer::new(code.as_str());
let parser = grammar::ProgramParser::new();
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);
@ -156,7 +168,11 @@ fn main() -> Result<()> {
let code = fs::read_to_string(&input)?;
let lexer = Lexer::new(&code[..]);
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);

View file

@ -2,21 +2,30 @@ use std::collections::{HashMap, HashSet};
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)]
struct Storage {
structs: HashMap<String, HashMap<String, TypeExp>>,
functions: HashMap<String, Function>,
}
// problem with scopes,
// let x = 2;
// let x = 2i64;
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?)
// 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();
// 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() {
if let Statement::Function(function) = statement {
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 (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?
function.scope_type_info = new_scope_vars
.into_iter()
@ -60,6 +67,7 @@ pub fn type_inference(ast: &mut ast::Program) {
.collect();
}
}
Ok(())
}
/// Finds variable types in the scope, returns newly created variables to handle shadowing
@ -68,7 +76,7 @@ fn type_inference_scope(
scope_vars: &ScopeMap,
func: &Function,
storage: &Storage,
) -> (ScopeMap, HashSet<String>) {
) -> Result<(ScopeMap, HashSet<String>), TypeError> {
let mut scope_vars = scope_vars.clone();
let mut new_vars: HashSet<String> = HashSet::new();
@ -78,11 +86,11 @@ fn type_inference_scope(
name,
value,
value_type,
span: _,
span,
} => {
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) {
scope_vars.insert(name.clone(), vec![]);
@ -94,27 +102,31 @@ fn type_inference_scope(
var.push(exp_type);
} else {
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());
}
}
Statement::Mutate {
name,
value,
span: _,
} => {
Statement::Mutate { name, value, span } => {
if !scope_vars.contains_key(name) {
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();
if var.is_none() {
*var = exp_type;
} 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 {
@ -129,10 +141,10 @@ fn type_inference_scope(
&mut scope_vars,
storage,
Some(TypeExp::Boolean),
);
)?;
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() {
// 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 {
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() {
// not a new var within the scope (shadowing), so type info is valid
@ -170,7 +182,7 @@ fn type_inference_scope(
&mut scope_vars,
storage,
func.return_type.clone(),
);
)?;
}
}
Statement::Function(_) => unreachable!(),
@ -178,7 +190,7 @@ fn type_inference_scope(
}
}
(scope_vars, new_vars)
Ok((scope_vars, new_vars))
}
fn type_inference_expression(
@ -186,8 +198,8 @@ fn type_inference_expression(
scope_vars: &mut ScopeMap,
storage: &Storage,
expected_type: Option<TypeExp>,
) -> Option<TypeExp> {
match exp {
) -> Result<Option<TypeExp>, TypeError> {
Ok(match exp {
Expression::Literal(lit) => {
match lit {
ast::LiteralValue::String(_) => None, // todo
@ -214,7 +226,13 @@ fn type_inference_expression(
*var = expected_type.clone();
expected_type
} 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
} else {
var.clone()
@ -229,7 +247,7 @@ fn type_inference_expression(
for (i, arg) in args.iter().enumerate() {
let arg_type = func.params[i].type_exp.clone();
// 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
@ -238,15 +256,19 @@ fn type_inference_expression(
ast::OpCode::Eq | ast::OpCode::Ne => Some(TypeExp::Boolean),
_ => {
let lhs_type =
type_inference_expression(lhs, scope_vars, storage, expected_type.clone());
let rhs_type = type_inference_expression(rhs, scope_vars, storage, expected_type);
type_inference_expression(lhs, scope_vars, storage, expected_type.clone())?;
let rhs_type = type_inference_expression(rhs, scope_vars, storage, expected_type)?;
if lhs_type.is_some() && rhs_type.is_some() {
assert_eq!(lhs_type, rhs_type, "types should match");
if lhs_type.is_some() && rhs_type.is_some() && lhs_type != rhs_type {
Err(TypeError::Mismatch {
found: rhs_type.clone().unwrap(),
expected: lhs_type.clone().unwrap(),
span: (0, 0), // todo
})?;
}
lhs_type.or(rhs_type)
}
},
}
})
}