refactor: migrate all errors to new struct
This commit is contained in:
parent
1a7dd72783
commit
15030635fb
@ -30,6 +30,8 @@
|
||||
- [x] Typecheck while loops
|
||||
- [x] Include Ariadne for error reporting
|
||||
- [x] Migrate lexic errors to new error interface
|
||||
- [x] Migrate syntax errors to new error interface
|
||||
- [x] Migrate semantic errors to new error interface
|
||||
|
||||
|
||||
## v0.1.1
|
||||
|
2
Cargo.lock
generated
2
Cargo.lock
generated
@ -152,7 +152,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thp"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
dependencies = [
|
||||
"ariadne",
|
||||
"colored",
|
||||
|
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "thp"
|
||||
version = "0.1.1"
|
||||
version = "0.1.2"
|
||||
edition = "2021"
|
||||
|
||||
|
||||
|
@ -17,4 +17,10 @@
|
||||
0x000015: Invalid for loop
|
||||
0x000016: Invalid if condition
|
||||
0x000017: Incomplete block
|
||||
0x000018: Missing reference
|
||||
0x000019: Invalid reference
|
||||
0x000020: Unknown error (compiler todo)
|
||||
0x000021: Mismatched types
|
||||
0x000022: Duplicated reference
|
||||
0x000023: Mismatched argument count
|
||||
|
||||
|
@ -18,6 +18,12 @@ pub const SYNTAX_INVALID_FUNCTION_DECLARATION: u32 = 14;
|
||||
pub const SYNTAX_INVALID_FOR_LOOP: u32 = 15;
|
||||
pub const SYNTAX_INVALID_IF_CONDITION: u32 = 16;
|
||||
pub const SYNTAX_INCOMPLETE_BLOCK: u32 = 17;
|
||||
pub const SEMANTIC_MISSING_REFERENCE: u32 = 18;
|
||||
pub const SEMANTIC_INVALID_REFERENCE: u32 = 19;
|
||||
pub const COMPILER_TODO: u32 = 20;
|
||||
pub const SEMANTIC_MISMATCHED_TYPES: u32 = 21;
|
||||
pub const SEMANTIC_DUPLICATED_REFERENCE: u32 = 22;
|
||||
pub const SEMANTIC_MISMATCHED_ARGUMENT_COUNT: u32 = 23;
|
||||
|
||||
/// Reads the error codes from the error code list
|
||||
pub fn error_code_to_string() -> String {
|
||||
|
@ -1,170 +0,0 @@
|
||||
use ariadne::{Color, Label, Report, ReportKind, Source};
|
||||
|
||||
use super::{LexError, PrintableError};
|
||||
use std::collections::VecDeque;
|
||||
|
||||
impl PrintableError for LexError {
|
||||
fn get_error_str(&self, chars: &Vec<char>) -> String {
|
||||
let line_number = get_line_number(chars, self.position);
|
||||
let (erroneous_code, column_number_zero) = get_line(chars, self.position);
|
||||
let column_number = column_number_zero + 1;
|
||||
|
||||
let line_number_whitespace = " ".repeat(line_number.to_string().len());
|
||||
let whitespace = " ".repeat(column_number_zero);
|
||||
let reason = &self.reason;
|
||||
|
||||
format!(
|
||||
r#"
|
||||
{line_number_whitespace} |
|
||||
{line_number } | {erroneous_code}
|
||||
{line_number_whitespace} | {whitespace}^
|
||||
|
||||
{reason} at line {line_number}:{column_number}"#,
|
||||
)
|
||||
}
|
||||
|
||||
fn print_ariadne(&self, source: &String) {
|
||||
let report = Report::build(ReportKind::Error, "sample.thp", self.position)
|
||||
.with_label(
|
||||
Label::new(("sample.thp", self.position..self.end_position))
|
||||
.with_message(self.reason.clone())
|
||||
.with_color(Color::Red),
|
||||
)
|
||||
.finish();
|
||||
report.eprint(("sample.thp", Source::from(source))).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
/// Extracts a line of code from `chars` and the number of characters in the back.
|
||||
/// `pos` indicates a position, from where to extract the line.
|
||||
///
|
||||
/// Ex. Given:
|
||||
/// - `input = "first line\nsecond line\nthird line"`
|
||||
/// - `pos = 15`
|
||||
///
|
||||
/// this function should return `("second line", 4)`
|
||||
fn get_line(chars: &Vec<char>, pos: usize) -> (String, usize) {
|
||||
let mut result_chars = VecDeque::<char>::new();
|
||||
|
||||
// Push chars to the front until a new line is found
|
||||
// TODO: refactor
|
||||
let mut before_pos = pos;
|
||||
loop {
|
||||
let current_char = chars[before_pos];
|
||||
|
||||
if current_char == '\n' {
|
||||
// This is important because before_pos will be used to calculate
|
||||
// the number of chars before pos
|
||||
before_pos += 1;
|
||||
break;
|
||||
}
|
||||
|
||||
result_chars.push_front(current_char);
|
||||
|
||||
if before_pos == 0 {
|
||||
break;
|
||||
}
|
||||
|
||||
before_pos -= 1;
|
||||
}
|
||||
|
||||
// Push chars to the end until a new line is found
|
||||
let mut after_pos = pos + 1;
|
||||
let char_count = chars.len();
|
||||
while after_pos < char_count {
|
||||
let current_char = chars[after_pos];
|
||||
|
||||
if current_char == '\n' {
|
||||
break;
|
||||
}
|
||||
|
||||
result_chars.push_back(current_char);
|
||||
after_pos += 1;
|
||||
}
|
||||
|
||||
(result_chars.iter().collect::<String>(), pos - before_pos)
|
||||
}
|
||||
|
||||
fn get_line_number(chars: &Vec<char>, target_pos: usize) -> usize {
|
||||
let mut count = 1;
|
||||
|
||||
for (pos, char) in chars.iter().enumerate() {
|
||||
if pos >= target_pos {
|
||||
break;
|
||||
}
|
||||
|
||||
if *char == '\n' {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
count
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::lexic;
|
||||
|
||||
#[test]
|
||||
fn test_error_msg() {
|
||||
let input = String::from("val name' = 20");
|
||||
let result = lexic::get_tokens(&input);
|
||||
|
||||
match result {
|
||||
Ok(_) => assert!(false),
|
||||
Err(err_data) => {
|
||||
let chars: Vec<char> = input.chars().into_iter().collect();
|
||||
let err_str = err_data.get_error_str(&chars);
|
||||
|
||||
let expected_str = format!(
|
||||
r#"
|
||||
|
|
||||
1 | val name' = 20
|
||||
| ^
|
||||
|
||||
Illegal character `'` (escaped: \') at line 1:9"#,
|
||||
);
|
||||
|
||||
assert_eq!(expected_str, err_str,);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_extract_line() {
|
||||
let input = String::from("first line\nsecond line\nthird line");
|
||||
let chars: Vec<char> = input.chars().into_iter().collect();
|
||||
|
||||
let (result, back_count) = get_line(&chars, 15);
|
||||
|
||||
assert_eq!("second line", result);
|
||||
assert_eq!(4, back_count);
|
||||
|
||||
let input = String::from("val binding = 322");
|
||||
let chars: Vec<char> = input.chars().into_iter().collect();
|
||||
|
||||
let (result, back_count) = get_line(&chars, 6);
|
||||
|
||||
assert_eq!("val binding = 322", result);
|
||||
assert_eq!(6, back_count);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_get_line_number() {
|
||||
let input = String::from("one\ntwo\nthree\nfour\nfive\nsix\nseven\neight\nnine\nten");
|
||||
let chars: Vec<char> = input.chars().into_iter().collect();
|
||||
|
||||
let line_number = get_line_number(&chars, 11);
|
||||
assert_eq!(3, line_number);
|
||||
|
||||
let line_number = get_line_number(&chars, 0);
|
||||
assert_eq!(1, line_number);
|
||||
|
||||
let line_number = get_line_number(&chars, 3);
|
||||
assert_eq!(1, line_number);
|
||||
|
||||
let line_number = get_line_number(&chars, 15);
|
||||
assert_eq!(4, line_number);
|
||||
}
|
||||
}
|
@ -5,9 +5,7 @@ use serde::Serialize;
|
||||
|
||||
use self::semantic_error::SemanticError;
|
||||
|
||||
mod lex_error;
|
||||
pub mod semantic_error;
|
||||
mod syntax_error;
|
||||
mod utils;
|
||||
|
||||
pub mod error_messages;
|
||||
@ -38,22 +36,7 @@ pub struct ErrorLabel {
|
||||
pub enum MistiError {
|
||||
Lex(ErrorContainer),
|
||||
Syntax(ErrorContainer),
|
||||
Semantic(SemanticError),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct LexError {
|
||||
pub position: usize,
|
||||
// TODO: Add and end position
|
||||
pub end_position: usize,
|
||||
pub reason: String,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Debug)]
|
||||
pub struct SyntaxError {
|
||||
pub error_start: usize,
|
||||
pub error_end: usize,
|
||||
pub reason: String,
|
||||
Semantic(ErrorContainer),
|
||||
}
|
||||
|
||||
impl PrintableError for MistiError {
|
||||
|
@ -1,78 +0,0 @@
|
||||
use ariadne::{Color, Label, Report, ReportKind, Source};
|
||||
|
||||
use super::utils::{get_line, get_line_number};
|
||||
use super::{PrintableError, SyntaxError};
|
||||
|
||||
impl PrintableError for SyntaxError {
|
||||
fn get_error_str(&self, chars: &Vec<char>) -> String {
|
||||
let (line, before, length) = get_line(chars, self.error_start, self.error_end);
|
||||
|
||||
let line_number = get_line_number(chars, self.error_start);
|
||||
let line_number_whitespace = " ".repeat(line_number.to_string().len());
|
||||
|
||||
let whitespace = vec![' '; before].iter().collect::<String>();
|
||||
let indicator = vec!['^'; length].iter().collect::<String>();
|
||||
let reason = &self.reason;
|
||||
|
||||
format!(
|
||||
r#"
|
||||
{line_number_whitespace} |
|
||||
{line_number } | {line}
|
||||
{line_number_whitespace} | {whitespace}{indicator}
|
||||
|
||||
{reason} at line {line_number}:{before}"#,
|
||||
)
|
||||
}
|
||||
|
||||
fn print_ariadne(&self, source: &String) {
|
||||
let report = Report::build(ReportKind::Error, "sample.thp", self.error_start)
|
||||
.with_label(
|
||||
Label::new(("sample.thp", self.error_start..self.error_end))
|
||||
.with_message(self.reason.clone())
|
||||
.with_color(Color::Red),
|
||||
)
|
||||
.finish();
|
||||
|
||||
report.eprint(("sample.thp", Source::from(source))).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{error_handling::MistiError, lexic::get_tokens, syntax::build_ast};
|
||||
|
||||
fn _get_error_data(input: String) -> (Vec<char>, MistiError) {
|
||||
let tokens = get_tokens(&input).unwrap();
|
||||
let error_holder = build_ast(&tokens);
|
||||
|
||||
match error_holder {
|
||||
Ok(_) => panic!(
|
||||
"syntax_error test: Input expected to throw error didn't:\n\n{}",
|
||||
input
|
||||
),
|
||||
Err(error) => {
|
||||
let chars: Vec<char> = input.chars().into_iter().collect();
|
||||
|
||||
(chars, error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn should_get_line() {
|
||||
let input: Vec<char> = String::from("\n\nval number == 50\n\n")
|
||||
.chars()
|
||||
.into_iter()
|
||||
.collect();
|
||||
|
||||
let start_position = 13;
|
||||
let end_position = 15;
|
||||
|
||||
let (line, before, length) = get_line(&input, start_position, end_position);
|
||||
|
||||
assert_eq!("val number == 50", line);
|
||||
assert_eq!(11, before);
|
||||
assert_eq!(2, length);
|
||||
}
|
||||
}
|
@ -1,5 +1,8 @@
|
||||
use crate::{
|
||||
error_handling::{semantic_error::SemanticError, MistiError},
|
||||
error_handling::{
|
||||
error_messages::SEMANTIC_DUPLICATED_REFERENCE, semantic_error::SemanticError,
|
||||
ErrorContainer, ErrorLabel, MistiError,
|
||||
},
|
||||
semantic::{
|
||||
impls::SemanticCheck,
|
||||
types::{Type, Typed},
|
||||
@ -17,16 +20,19 @@ impl SemanticCheck for VariableBinding<'_> {
|
||||
// TODO: Define if variables can be redeclared.
|
||||
// If so, it is irrelevant to check if the variable is already defined
|
||||
if scope.test(binding_name) {
|
||||
let error = SemanticError {
|
||||
error_start: self.identifier.position,
|
||||
error_end: self.identifier.get_end_position(),
|
||||
reason: format!(
|
||||
"Duplicated: A symbol with name {} was already defined",
|
||||
binding_name
|
||||
),
|
||||
let label = ErrorLabel {
|
||||
message: String::from("A reference with this name was already defined"),
|
||||
start: self.identifier.position,
|
||||
end: self.identifier.get_end_position(),
|
||||
};
|
||||
|
||||
return Err(MistiError::Semantic(error));
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_DUPLICATED_REFERENCE,
|
||||
error_offset: self.identifier.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(MistiError::Semantic(econtainer));
|
||||
}
|
||||
|
||||
// This gets the datatype of the assigned expression,
|
||||
@ -41,16 +47,22 @@ impl SemanticCheck for VariableBinding<'_> {
|
||||
|
||||
// Both the declared & actual datatypes must be the same
|
||||
if datatype != expression_datatype {
|
||||
let error = SemanticError {
|
||||
error_start: self.identifier.position,
|
||||
error_end: self.identifier.get_end_position(),
|
||||
reason: format!(
|
||||
let label = ErrorLabel {
|
||||
message: format!(
|
||||
"The variable `{}` was declared as `{:?}` but its expression has type `{:?}`",
|
||||
binding_name, datatype, expression_datatype
|
||||
),
|
||||
start: self.identifier.position,
|
||||
end: self.identifier.get_end_position(),
|
||||
};
|
||||
|
||||
return Err(MistiError::Semantic(error));
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_DUPLICATED_REFERENCE,
|
||||
error_offset: self.identifier.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(MistiError::Semantic(econtainer));
|
||||
}
|
||||
|
||||
scope.insert(binding_name.clone(), datatype);
|
||||
|
@ -1,5 +1,8 @@
|
||||
use crate::{
|
||||
error_handling::{semantic_error::SemanticError, MistiError},
|
||||
error_handling::{
|
||||
error_messages::SEMANTIC_MISMATCHED_TYPES, semantic_error::SemanticError, ErrorContainer,
|
||||
ErrorLabel, MistiError,
|
||||
},
|
||||
semantic::{
|
||||
impls::SemanticCheck,
|
||||
symbol_table::SymbolTable,
|
||||
@ -17,14 +20,22 @@ impl SemanticCheck for Conditional<'_> {
|
||||
let if_condition_type = if_condition.get_type(scope)?;
|
||||
if !if_condition_type.equals(&bool_type) {
|
||||
let (error_start, error_end) = if_condition.get_position();
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start,
|
||||
error_end,
|
||||
reason: format!(
|
||||
let label = ErrorLabel {
|
||||
message: format!(
|
||||
"Expected a condition of type Bool, found {:?}",
|
||||
if_condition_type
|
||||
),
|
||||
}));
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISMATCHED_TYPES,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(MistiError::Semantic(econtainer));
|
||||
}
|
||||
|
||||
// Check if block
|
||||
@ -37,14 +48,22 @@ impl SemanticCheck for Conditional<'_> {
|
||||
let condition_type = condition.get_type(scope)?;
|
||||
if !condition_type.equals(&bool_type) {
|
||||
let (error_start, error_end) = condition.get_position();
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start,
|
||||
error_end,
|
||||
reason: format!(
|
||||
let label = ErrorLabel {
|
||||
message: format!(
|
||||
"Expected a condition of type Bool, found {:?}",
|
||||
condition_type
|
||||
),
|
||||
}));
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISMATCHED_TYPES,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(MistiError::Semantic(econtainer));
|
||||
}
|
||||
|
||||
else_if_member.body.check_semantics(scope)?;
|
||||
|
@ -1,5 +1,11 @@
|
||||
use crate::{
|
||||
error_handling::{semantic_error::SemanticError, MistiError},
|
||||
error_handling::{
|
||||
error_messages::{
|
||||
COMPILER_TODO, SEMANTIC_INVALID_REFERENCE, SEMANTIC_MISMATCHED_ARGUMENT_COUNT,
|
||||
SEMANTIC_MISMATCHED_TYPES,
|
||||
},
|
||||
ErrorContainer, ErrorLabel, MistiError,
|
||||
},
|
||||
semantic::{
|
||||
impls::SemanticCheck,
|
||||
symbol_table::SymbolTable,
|
||||
@ -22,15 +28,23 @@ impl SemanticCheck for Expression<'_> {
|
||||
if parameters.len() != arguments.len() {
|
||||
let (error_start, error_end) = f.arguments.get_position();
|
||||
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start,
|
||||
error_end,
|
||||
reason: format!(
|
||||
let label = ErrorLabel {
|
||||
message: format!(
|
||||
"Expected {} arguments, got {}",
|
||||
parameters.len(),
|
||||
arguments.len(),
|
||||
),
|
||||
}));
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISMATCHED_ARGUMENT_COUNT,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(MistiError::Semantic(econtainer));
|
||||
}
|
||||
|
||||
// Check that each argument matches the required datatype
|
||||
@ -42,14 +56,22 @@ impl SemanticCheck for Expression<'_> {
|
||||
if !argument_datatype.is_value(parameter) {
|
||||
// The argument and the parameter have diferent types
|
||||
let (error_start, error_end) = argument.get_position();
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start,
|
||||
error_end,
|
||||
reason: format!(
|
||||
let label = ErrorLabel {
|
||||
message: format!(
|
||||
"Expected a {}, got {:?}",
|
||||
parameter, argument_datatype
|
||||
),
|
||||
}));
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISMATCHED_TYPES,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(MistiError::Semantic(econtainer));
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,14 +79,22 @@ impl SemanticCheck for Expression<'_> {
|
||||
}
|
||||
_ => {
|
||||
let (error_start, error_end) = fun.get_position();
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start,
|
||||
error_end,
|
||||
reason: format!(
|
||||
let label = ErrorLabel {
|
||||
message: format!(
|
||||
"Expected a function type, got {:?}",
|
||||
function_datatype
|
||||
),
|
||||
}));
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISMATCHED_TYPES,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(MistiError::Semantic(econtainer));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -87,21 +117,37 @@ impl SemanticCheck for Expression<'_> {
|
||||
} else {
|
||||
// Error: unary negation can only be applied to a Bool
|
||||
let (error_start, error_end) = expression.get_position();
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start,
|
||||
error_end,
|
||||
reason: format!("Expected a Bool, got a {}", t),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: format!("Expected a Bool, got {}", t),
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISMATCHED_TYPES,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(MistiError::Semantic(econtainer));
|
||||
}
|
||||
}
|
||||
("!", Type::Function(_, _)) => {
|
||||
// Error: unary negation can only be applied to a Bool
|
||||
let (error_start, error_end) = expression.get_position();
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start,
|
||||
error_end,
|
||||
reason: format!("Expected a Bool, got a function",),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: format!("Expected a Bool, got a function"),
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISMATCHED_TYPES,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(MistiError::Semantic(econtainer));
|
||||
}
|
||||
("-", Type::Value(t)) => {
|
||||
if t == "Int" || t == "Float" {
|
||||
@ -110,21 +156,37 @@ impl SemanticCheck for Expression<'_> {
|
||||
} else {
|
||||
// Error: unary negation can only be applied to a Number
|
||||
let (error_start, error_end) = expression.get_position();
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start,
|
||||
error_end,
|
||||
reason: format!("Expected a Float or Int, got a {}", t),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: format!("Expected a Float or Int, got a {}", t),
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISMATCHED_TYPES,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(MistiError::Semantic(econtainer));
|
||||
}
|
||||
}
|
||||
("-", Type::Function(_, _)) => {
|
||||
// Error: unary negation can only be applied to a Bool
|
||||
let (error_start, error_end) = expression.get_position();
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start,
|
||||
error_end,
|
||||
reason: format!("Expected a Float or Int, got a function",),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: format!("Expected a Float or Int, got a function"),
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISMATCHED_TYPES,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(MistiError::Semantic(econtainer));
|
||||
}
|
||||
(op, _) => {
|
||||
// Compiler error: something that shouldn't be
|
||||
@ -146,11 +208,19 @@ impl SemanticCheck for Expression<'_> {
|
||||
// If the operator is not found its a user error,
|
||||
// because we allow arbitrary operators
|
||||
let (error_start, error_end) = (op.position, op.get_end_position());
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start,
|
||||
error_end,
|
||||
reason: format!("The binary operator {} does not exist", op.value),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: format!("The binary operator {} does not exist", op.value),
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_INVALID_REFERENCE,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(MistiError::Semantic(econtainer));
|
||||
}
|
||||
};
|
||||
|
||||
@ -168,26 +238,42 @@ impl SemanticCheck for Expression<'_> {
|
||||
|
||||
if !left_expr_type.is_value(&op_params[0]) {
|
||||
let (error_start, error_end) = left_expr.get_position();
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start,
|
||||
error_end,
|
||||
reason: format!(
|
||||
let label = ErrorLabel {
|
||||
message: format!(
|
||||
"Expected a {}, got a {:?} on the left side of the {} operator",
|
||||
op_params[0], left_expr_type, op.value
|
||||
),
|
||||
}));
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISMATCHED_TYPES,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(MistiError::Semantic(econtainer));
|
||||
}
|
||||
|
||||
if !right_expr_type.is_value(&op_params[1]) {
|
||||
let (error_start, error_end) = right_expr.get_position();
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start,
|
||||
error_end,
|
||||
reason: format!(
|
||||
let label = ErrorLabel {
|
||||
message: format!(
|
||||
"Expected a {}, got a {:?} on the right side of the {} operator",
|
||||
op_params[1], left_expr_type, op.value
|
||||
),
|
||||
}));
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISMATCHED_TYPES,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(MistiError::Semantic(econtainer));
|
||||
}
|
||||
|
||||
// After all these checks, we are ok
|
||||
@ -206,13 +292,21 @@ impl SemanticCheck for Expression<'_> {
|
||||
// TODO: if the array is empty then its
|
||||
// datatype should be determined by its usage.
|
||||
if arr.exps.is_empty() {
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start: arr.start,
|
||||
error_end: arr.end,
|
||||
reason: format!(
|
||||
let label = ErrorLabel {
|
||||
message: format!(
|
||||
"An array must have at least 1 element to determine its type. This will be fixed later."
|
||||
),
|
||||
}));
|
||||
start: arr.start,
|
||||
end: arr.end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: COMPILER_TODO,
|
||||
error_offset: arr.start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(MistiError::Semantic(econtainer));
|
||||
}
|
||||
|
||||
let mut expressions = arr.exps.iter();
|
||||
@ -227,15 +321,23 @@ impl SemanticCheck for Expression<'_> {
|
||||
|
||||
// error, found an item with a diferent datatype
|
||||
let (error_start, error_end) = exp.get_position();
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start,
|
||||
error_end,
|
||||
reason: format!(
|
||||
let label = ErrorLabel {
|
||||
message: format!(
|
||||
"All elements of an array must have the same datatype. Expected {:?}, got {:?}",
|
||||
first_type,
|
||||
exp_type,
|
||||
),
|
||||
}));
|
||||
),
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: COMPILER_TODO,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(MistiError::Semantic(econtainer));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,7 @@
|
||||
use crate::{
|
||||
error_handling::{semantic_error::SemanticError, MistiError},
|
||||
error_handling::{
|
||||
error_messages::SEMANTIC_MISMATCHED_TYPES, ErrorContainer, ErrorLabel, MistiError,
|
||||
},
|
||||
semantic::{
|
||||
impls::SemanticCheck,
|
||||
symbol_table::SymbolTable,
|
||||
@ -30,11 +32,19 @@ impl SemanticCheck for ForLoop<'_> {
|
||||
_ => {
|
||||
// error, types other than an Array are not supported
|
||||
let (error_start, error_end) = self.collection.get_position();
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start,
|
||||
error_end,
|
||||
reason: format!("Only Array[T] are allowed as a for-loop collection."),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Only Arrays are allowed here"),
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISMATCHED_TYPES,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(MistiError::Semantic(econtainer));
|
||||
}
|
||||
};
|
||||
let item_type = &item_type[0];
|
||||
|
@ -1,5 +1,7 @@
|
||||
use crate::{
|
||||
error_handling::{semantic_error::SemanticError, MistiError},
|
||||
error_handling::{
|
||||
error_messages::SEMANTIC_DUPLICATED_REFERENCE, ErrorContainer, ErrorLabel, MistiError,
|
||||
},
|
||||
semantic::{impls::SemanticCheck, symbol_table::SymbolTable, types::Type},
|
||||
syntax::ast::FunctionDeclaration,
|
||||
};
|
||||
@ -13,16 +15,24 @@ impl SemanticCheck for FunctionDeclaration<'_> {
|
||||
|
||||
// Check that the function is not already defined
|
||||
if scope.test(&function_name) {
|
||||
let error = SemanticError {
|
||||
error_start: self.identifier.position,
|
||||
error_end: self.identifier.get_end_position(),
|
||||
reason: format!(
|
||||
"Duplicated: A symbol with name {} was already defined",
|
||||
function_name
|
||||
let (error_start, error_end) =
|
||||
(self.identifier.position, self.identifier.get_end_position());
|
||||
let label = ErrorLabel {
|
||||
message: format!(
|
||||
"A symbol with name {} was already defined at this scope",
|
||||
function_name,
|
||||
),
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
|
||||
return Err(MistiError::Semantic(error));
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_DUPLICATED_REFERENCE,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(MistiError::Semantic(econtainer));
|
||||
}
|
||||
|
||||
// Create a new scope and use it in the function block
|
||||
|
@ -1,5 +1,7 @@
|
||||
use crate::{
|
||||
error_handling::{semantic_error::SemanticError, MistiError},
|
||||
error_handling::{
|
||||
error_messages::SEMANTIC_MISMATCHED_TYPES, ErrorContainer, ErrorLabel, MistiError,
|
||||
},
|
||||
semantic::{
|
||||
impls::SemanticCheck,
|
||||
symbol_table::SymbolTable,
|
||||
@ -16,14 +18,23 @@ impl SemanticCheck for WhileLoop<'_> {
|
||||
|
||||
if !condition_type.equals(&Type::Value("Bool".into())) {
|
||||
let (error_start, error_end) = condition.get_position();
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start,
|
||||
error_end,
|
||||
reason: format!(
|
||||
|
||||
let label = ErrorLabel {
|
||||
message: format!(
|
||||
"Expected a condition of type Bool, found {:?}",
|
||||
condition_type
|
||||
),
|
||||
}));
|
||||
start: error_start,
|
||||
end: error_end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISMATCHED_TYPES,
|
||||
error_offset: error_start,
|
||||
labels: vec![label],
|
||||
note: Some(String::from("THP does not have truthy/falsey values.")),
|
||||
help: None,
|
||||
};
|
||||
return Err(MistiError::Semantic(econtainer));
|
||||
}
|
||||
|
||||
// TODO: Define scoping rules for while loops
|
||||
|
@ -1,5 +1,11 @@
|
||||
use crate::{
|
||||
error_handling::{semantic_error::SemanticError, MistiError},
|
||||
error_handling::{
|
||||
error_messages::{
|
||||
COMPILER_TODO, SEMANTIC_INVALID_REFERENCE, SEMANTIC_MISMATCHED_TYPES,
|
||||
SEMANTIC_MISSING_REFERENCE,
|
||||
},
|
||||
ErrorContainer, ErrorLabel, MistiError,
|
||||
},
|
||||
semantic::symbol_table::SymbolTable,
|
||||
syntax::ast::Expression,
|
||||
};
|
||||
@ -19,11 +25,19 @@ impl Typed for Expression<'_> {
|
||||
let datatype = match scope.get_type(&identifier.value) {
|
||||
Some(x) => x,
|
||||
None => {
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start: identifier.position,
|
||||
error_end: identifier.get_end_position(),
|
||||
reason: format!("Cannot find `{}` in this scope.", identifier.value),
|
||||
}))
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Cannot find this identifier in this scope"),
|
||||
start: identifier.position,
|
||||
end: identifier.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISSING_REFERENCE,
|
||||
error_offset: identifier.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(MistiError::Semantic(econtainer));
|
||||
}
|
||||
};
|
||||
|
||||
@ -43,20 +57,44 @@ impl Typed for Expression<'_> {
|
||||
// not the function itself
|
||||
Ok(Type::Value(return_type))
|
||||
}
|
||||
Some(_) => Err(MistiError::Semantic(SemanticError {
|
||||
error_start: id.position,
|
||||
error_end: id.get_end_position(),
|
||||
reason: format!("Expected `{}` to be a function", &id.value),
|
||||
})),
|
||||
None => Err(MistiError::Semantic(SemanticError {
|
||||
error_start: id.position,
|
||||
error_end: id.get_end_position(),
|
||||
reason: format!("Cannot find `{}` in this scope.", id.value),
|
||||
})),
|
||||
Some(_) => {
|
||||
let label = ErrorLabel {
|
||||
message: String::from(
|
||||
"Expected this identifier to be a function",
|
||||
),
|
||||
start: id.position,
|
||||
end: id.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_INVALID_REFERENCE,
|
||||
error_offset: id.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(MistiError::Semantic(econtainer));
|
||||
}
|
||||
None => {
|
||||
let label = ErrorLabel {
|
||||
message: String::from(
|
||||
"Cannot find this identifier in this scope",
|
||||
),
|
||||
start: id.position,
|
||||
end: id.get_end_position(),
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_INVALID_REFERENCE,
|
||||
error_offset: id.position,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(MistiError::Semantic(econtainer));
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => unimplemented!(
|
||||
"Get datatype of an expression that resolves into a function call"
|
||||
"Get datatype of an expression that may resolve into a function call"
|
||||
),
|
||||
}
|
||||
}
|
||||
@ -64,35 +102,59 @@ impl Typed for Expression<'_> {
|
||||
let expr_type = match exp.get_type(scope) {
|
||||
Ok(t) => t,
|
||||
Err(_reason) => {
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start: 0,
|
||||
error_end: 1,
|
||||
reason: format!("Error getting type of expression"),
|
||||
}))
|
||||
let label = ErrorLabel {
|
||||
message: String::from("Error getting type of this expression"),
|
||||
// TODO: Fix these positions
|
||||
start: 0,
|
||||
end: 1,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: COMPILER_TODO,
|
||||
error_offset: 0,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(MistiError::Semantic(econtainer));
|
||||
}
|
||||
};
|
||||
|
||||
// Only supported unary operator: - & !
|
||||
if op.value == "-" {
|
||||
if !expr_type.is_value("Int") && !expr_type.is_value("Float") {
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start: 0,
|
||||
error_end: 1,
|
||||
reason: format!(
|
||||
"Expected a Int or Float after unary `-`, got {:?}",
|
||||
expr_type
|
||||
),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: format!("Expected an `Int` or `Float`, got {:?}", expr_type),
|
||||
// TODO: Fix positioning
|
||||
start: 0,
|
||||
end: 1,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISMATCHED_TYPES,
|
||||
error_offset: 0,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(MistiError::Semantic(econtainer));
|
||||
} else {
|
||||
return Ok(Type::Value("Int".into()));
|
||||
}
|
||||
} else if op.value == "!" {
|
||||
if !expr_type.is_value("Bool") {
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start: 0,
|
||||
error_end: 1,
|
||||
reason: format!("Expected a Bool after unary `!`, got {:?}", expr_type),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: format!("Expected a `Bool`, got {:?}", expr_type),
|
||||
// TODO: Fix positioning
|
||||
start: 0,
|
||||
end: 1,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISMATCHED_TYPES,
|
||||
error_offset: 0,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(MistiError::Semantic(econtainer));
|
||||
} else {
|
||||
return Ok(Type::Value("Bool".into()));
|
||||
}
|
||||
@ -105,20 +167,27 @@ impl Typed for Expression<'_> {
|
||||
let t2 = exp2.get_type(scope)?;
|
||||
|
||||
// TODO: There's definitely a better way to do this
|
||||
// maybe store operators as functions?
|
||||
if operator.value == "+" && t1.is_value("Int") && t2.is_value("Int") {
|
||||
return Ok(Type::Value("Int".into()));
|
||||
} else if operator.value == "-" && t1.is_value("Int") && t2.is_value("Int") {
|
||||
return Ok(Type::Value("Int".into()));
|
||||
}
|
||||
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
// TODO: fix positions
|
||||
error_start: 0,
|
||||
error_end: 1,
|
||||
reason: format!(
|
||||
"Unsupported binary operator or invalid arguments to the operator."
|
||||
),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: format!("Unsupported binary operator"),
|
||||
// TODO: Fix positioning
|
||||
start: 0,
|
||||
end: 1,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISMATCHED_TYPES,
|
||||
error_offset: 0,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(MistiError::Semantic(econtainer));
|
||||
}
|
||||
Expression::Array(arr) => {
|
||||
// The first expression found determines the
|
||||
@ -129,13 +198,20 @@ impl Typed for Expression<'_> {
|
||||
// TODO: if the array is empty then its
|
||||
// datatype should be determined by its usage.
|
||||
if arr.exps.is_empty() {
|
||||
return Err(MistiError::Semantic(SemanticError {
|
||||
error_start: arr.start,
|
||||
error_end: arr.end,
|
||||
reason: format!(
|
||||
"An array must have at least 1 element to determine its type. This will be fixed later."
|
||||
),
|
||||
}));
|
||||
let label = ErrorLabel {
|
||||
message: format!("Compiler limit: Arrays must have at least 1 element to determine their type"),
|
||||
// TODO: Fix positioning
|
||||
start: arr.start,
|
||||
end: arr.end,
|
||||
};
|
||||
let econtainer = ErrorContainer {
|
||||
error_code: SEMANTIC_MISMATCHED_TYPES,
|
||||
error_offset: 0,
|
||||
labels: vec![label],
|
||||
note: None,
|
||||
help: None,
|
||||
};
|
||||
return Err(MistiError::Semantic(econtainer));
|
||||
}
|
||||
|
||||
// Just get the first type and use it
|
||||
|
Loading…
Reference in New Issue
Block a user