improve error reporting

This commit is contained in:
Sebastien Bourdeauducq 2020-03-27 16:54:49 +08:00
parent 894bbca38e
commit 8b7b012b27
1 changed files with 76 additions and 22 deletions

View File

@ -9,18 +9,41 @@ use inkwell::context::Context;
use inkwell::module::Module; use inkwell::module::Module;
use inkwell::targets::*; use inkwell::targets::*;
use inkwell::types; use inkwell::types;
use crate::inkwell::types::BasicType; use inkwell::types::BasicType;
use std::error::Error; use std::error::Error;
use std::fmt; use std::fmt;
use std::path::Path; use std::path::Path;
#[derive(Debug)] #[derive(Debug)]
struct CompileError; enum CompileErrorKind {
Unsupported(&'static str),
MissingTypeAnnotation,
UnknownTypeAnnotation
}
impl fmt::Display for CompileErrorKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
CompileErrorKind::Unsupported(feature)
=> write!(f, "The following Python feature is not supported by NAC3 {}: ", feature),
CompileErrorKind::MissingTypeAnnotation
=> write!(f, "Missing type annotation"),
CompileErrorKind::UnknownTypeAnnotation
=> write!(f, "Unknown type annotation"),
}
}
}
#[derive(Debug)]
struct CompileError {
location: ast::Location,
kind: CompileErrorKind,
}
impl fmt::Display for CompileError { impl fmt::Display for CompileError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Compilation error") write!(f, "Compilation error at {}: {}", self.location, self.kind)
} }
} }
@ -31,7 +54,8 @@ type CompileResult<T> = Result<T, CompileError>;
struct CodeGen<'ctx> { struct CodeGen<'ctx> {
context: &'ctx Context, context: &'ctx Context,
module: Module<'ctx>, module: Module<'ctx>,
builder: Builder<'ctx> builder: Builder<'ctx>,
current_source_location: ast::Location,
} }
impl<'ctx> CodeGen<'ctx> { impl<'ctx> CodeGen<'ctx> {
@ -39,7 +63,19 @@ impl<'ctx> CodeGen<'ctx> {
CodeGen { CodeGen {
context, context,
module: context.create_module("kernel"), module: context.create_module("kernel"),
builder: context.create_builder() builder: context.create_builder(),
current_source_location: ast::Location::default(),
}
}
fn set_source_location(&mut self, location: ast::Location) {
self.current_source_location = location;
}
fn compile_error(&self, kind: CompileErrorKind) -> CompileError {
CompileError {
location: self.current_source_location,
kind
} }
} }
@ -64,29 +100,42 @@ impl<'ctx> CodeGen<'ctx> {
is_async: bool, is_async: bool,
) -> CompileResult<()> { ) -> CompileResult<()> {
if is_async { if is_async {
return Err(CompileError) return Err(self.compile_error(CompileErrorKind::Unsupported("async functions")))
}
for decorator in decorator_list.iter() {
self.set_source_location(decorator.location);
if let ast::ExpressionType::Identifier { name } = &decorator.node {
if name != "kernel" && name != "portable" {
return Err(self.compile_error(CompileErrorKind::Unsupported("custom decorators")))
}
} else {
return Err(self.compile_error(CompileErrorKind::Unsupported("complex decorators")))
}
} }
let args_type = args.args.iter().map(|val| { let args_type = args.args.iter().map(|val| {
println!("{:?}", val.annotation); self.set_source_location(val.location);
if let Some(annotation) = &val.annotation { if let Some(annotation) = &val.annotation {
if let ast::ExpressionType::Identifier { name } = &annotation.node { if let ast::ExpressionType::Identifier { name } = &annotation.node {
Ok(self.get_basic_type(&name)?) Ok(self.get_basic_type(&name)?)
} else { } else {
Err(CompileError) Err(self.compile_error(CompileErrorKind::Unsupported("complex type annotation")))
} }
} else { } else {
Err(CompileError) Err(self.compile_error(CompileErrorKind::MissingTypeAnnotation))
} }
}).collect::<CompileResult<Vec<types::BasicTypeEnum>>>()?; }).collect::<CompileResult<Vec<types::BasicTypeEnum>>>()?;
let return_type = match returns { let return_type = if let Some(returns) = returns {
Some(ast::Located { location, node: ast::ExpressionType::Identifier { name }} ) self.set_source_location(returns.location);
=> if name == "None" { None } else { Some(self.get_basic_type(name)?) }, if let ast::ExpressionType::Identifier { name } = &returns.node {
Some(_) => return Err(CompileError), if name == "None" { None } else { Some(self.get_basic_type(name)?) }
None => None, } else {
return Err(self.compile_error(CompileErrorKind::Unsupported("complex type annotation")))
}
} else {
None
}; };
let i64_type = self.context.i64_type();
let fn_type = match return_type { let fn_type = match return_type {
Some(ty) => ty.fn_type(&args_type, false), Some(ty) => ty.fn_type(&args_type, false),
None => self.context.void_type().fn_type(&args_type, false) None => self.context.void_type().fn_type(&args_type, false)
@ -103,6 +152,8 @@ impl<'ctx> CodeGen<'ctx> {
} }
fn compile_statement(&mut self, statement: &ast::Statement) -> CompileResult<()> { fn compile_statement(&mut self, statement: &ast::Statement) -> CompileResult<()> {
self.set_source_location(statement.location);
use ast::StatementType::*; use ast::StatementType::*;
match &statement.node { match &statement.node {
FunctionDef { FunctionDef {
@ -116,7 +167,7 @@ impl<'ctx> CodeGen<'ctx> {
self.compile_function_def(name, args, body, decorator_list, returns, *is_async)?; self.compile_function_def(name, args, body, decorator_list, returns, *is_async)?;
}, },
Pass => (), Pass => (),
_ => return Err(CompileError), _ => return Err(self.compile_error(CompileErrorKind::Unsupported("special statement"))),
} }
Ok(()) Ok(())
} }
@ -143,16 +194,19 @@ impl<'ctx> CodeGen<'ctx> {
} }
} }
fn main() -> Result<(), Box<dyn Error>> { fn main() {
Target::initialize_all(&InitializationConfig::default()); Target::initialize_all(&InitializationConfig::default());
let ast = parser::parse_program("def foo(x: int32, y: int32) -> int32: return x + y")?; let ast = match parser::parse_program("def foo(x: int32, y: int32) -> int32: return x + y") {
println!("AST: {:?}", ast); Ok(ast) => ast,
Err(err) => { println!("{}", err); return; }
};
let context = Context::create(); let context = Context::create();
let mut codegen = CodeGen::new(&context); let mut codegen = CodeGen::new(&context);
codegen.compile_statement(&ast.statements[0])?; match codegen.compile_statement(&ast.statements[0]) {
codegen.output(); Ok(_) => (),
Err(err) => { println!("{}", err); return; }
Ok(()) }
codegen.output();
} }