-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Split typing.rs into abstract_type.rs and concrete_type.rs
- Loading branch information
Showing
14 changed files
with
343 additions
and
338 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
use std::ops::{Deref, Index}; | ||
|
||
use crate::{errors::ErrorCollector, file_position::{Span, SpanFile}, flattening::{BinaryOperator, UnaryOperator}, linker::{get_builtin_type, Linkable, NamedType, TypeUUID}}; | ||
|
||
/// This contains only the information that can be easily type-checked. | ||
/// | ||
/// Its most important components are the names and structure of types. | ||
/// | ||
/// What isn't included are the parameters of types. So Array Sizes for example. | ||
#[derive(Debug, Clone)] | ||
pub enum AbstractType { | ||
Error, | ||
Unknown, | ||
Named(TypeUUID), | ||
Array(Box<AbstractType>) | ||
} | ||
|
||
impl AbstractType { | ||
pub fn to_string<TypVec : Index<TypeUUID, Output = NamedType>>(&self, linker_types : &TypVec) -> String { | ||
match self { | ||
AbstractType::Error => { | ||
"{error}".to_owned() | ||
} | ||
AbstractType::Unknown => { | ||
"{unknown}".to_owned() | ||
} | ||
AbstractType::Named(id) => { | ||
linker_types[*id].get_full_name() | ||
} | ||
AbstractType::Array(sub) => sub.deref().to_string(linker_types) + "[]", | ||
} | ||
} | ||
pub fn contains_error_or_unknown<const CHECK_ERROR : bool, const CHECK_UNKNOWN : bool>(&self) -> bool { | ||
match self { | ||
AbstractType::Error => CHECK_ERROR, | ||
AbstractType::Unknown => CHECK_UNKNOWN, | ||
AbstractType::Named(_id) => false, | ||
AbstractType::Array(arr_box) => { | ||
arr_box.deref().contains_error_or_unknown::<CHECK_ERROR, CHECK_UNKNOWN>() | ||
} | ||
} | ||
} | ||
} | ||
|
||
|
||
pub const BOOL_TYPE : AbstractType = AbstractType::Named(get_builtin_type("bool")); | ||
pub const INT_TYPE : AbstractType = AbstractType::Named(get_builtin_type("int")); | ||
const ERROR_TYPE : AbstractType = AbstractType::Error; | ||
|
||
pub fn typecheck_unary_operator<TypVec : Index<TypeUUID, Output = NamedType>>(op : UnaryOperator, input_typ : &AbstractType, span : Span, linker_types : &TypVec, errors : &ErrorCollector) -> AbstractType { | ||
if op == UnaryOperator::Not { | ||
typecheck(input_typ, span, &BOOL_TYPE, "! input", linker_types, None, errors); | ||
BOOL_TYPE | ||
} else if op == UnaryOperator::Negate { | ||
typecheck(input_typ, span, &INT_TYPE, "- input", linker_types, None, errors); | ||
INT_TYPE | ||
} else { | ||
let gather_type = match op { | ||
UnaryOperator::And => BOOL_TYPE, | ||
UnaryOperator::Or => BOOL_TYPE, | ||
UnaryOperator::Xor => BOOL_TYPE, | ||
UnaryOperator::Sum => INT_TYPE, | ||
UnaryOperator::Product => INT_TYPE, | ||
_ => unreachable!() | ||
}; | ||
let arr_content_typ = typecheck_is_array_indexer(input_typ, span, linker_types, errors); | ||
typecheck(arr_content_typ, span, &gather_type, &format!("{op} input"), linker_types, None, errors); | ||
|
||
gather_type | ||
} | ||
} | ||
pub fn get_binary_operator_types(op : BinaryOperator) -> ((AbstractType, AbstractType), AbstractType) { | ||
match op { | ||
BinaryOperator::And => ((BOOL_TYPE, BOOL_TYPE), BOOL_TYPE), | ||
BinaryOperator::Or => ((BOOL_TYPE, BOOL_TYPE), BOOL_TYPE), | ||
BinaryOperator::Xor => ((BOOL_TYPE, BOOL_TYPE), BOOL_TYPE), | ||
BinaryOperator::Add => ((INT_TYPE, INT_TYPE), INT_TYPE), | ||
BinaryOperator::Subtract => ((INT_TYPE, INT_TYPE), INT_TYPE), | ||
BinaryOperator::Multiply => ((INT_TYPE, INT_TYPE), INT_TYPE), | ||
BinaryOperator::Divide => ((INT_TYPE, INT_TYPE), INT_TYPE), | ||
BinaryOperator::Modulo => ((INT_TYPE, INT_TYPE), INT_TYPE), | ||
BinaryOperator::Equals => ((INT_TYPE, INT_TYPE), BOOL_TYPE), | ||
BinaryOperator::NotEquals => ((INT_TYPE, INT_TYPE), BOOL_TYPE), | ||
BinaryOperator::GreaterEq => ((INT_TYPE, INT_TYPE), BOOL_TYPE), | ||
BinaryOperator::Greater => ((INT_TYPE, INT_TYPE), BOOL_TYPE), | ||
BinaryOperator::LesserEq => ((INT_TYPE, INT_TYPE), BOOL_TYPE), | ||
BinaryOperator::Lesser => ((INT_TYPE, INT_TYPE), BOOL_TYPE), | ||
} | ||
} | ||
|
||
fn type_compare(expected : &AbstractType, found : &AbstractType) -> bool { | ||
match (expected, found) { | ||
(AbstractType::Named(exp), AbstractType::Named(fnd)) => exp == fnd, | ||
(AbstractType::Array(exp), AbstractType::Array(fnd)) => { | ||
type_compare(&exp.deref(), &fnd.deref()) | ||
} | ||
(AbstractType::Error, _) | (_, AbstractType::Error) => true, // Just assume correct, because the other side has an error | ||
(AbstractType::Unknown, _) | (_, AbstractType::Unknown) => todo!("Type Unification"), | ||
_ => false, | ||
} | ||
} | ||
pub fn typecheck<TypVec : Index<TypeUUID, Output = NamedType>>(found : &AbstractType, span : Span, expected : &AbstractType, context : &str, linker_types : &TypVec, declared_here : Option<SpanFile>, errors : &ErrorCollector) { | ||
if !type_compare(expected, found) { | ||
let expected_name = expected.to_string(linker_types); | ||
let found_name = found.to_string(linker_types); | ||
let err_ref = errors.error(span, format!("Typing Error: {context} expects a {expected_name} but was given a {found_name}")); | ||
if let Some(declared_here) = declared_here { | ||
err_ref.info(declared_here, "Declared here"); | ||
} | ||
assert!(expected_name != found_name, "{expected_name} != {found_name}"); | ||
} | ||
} | ||
|
||
pub fn typecheck_is_array_indexer<'a, TypVec : Index<TypeUUID, Output = NamedType>>(arr_type : &'a AbstractType, span : Span, linker_types : &TypVec, errors : &ErrorCollector) -> &'a AbstractType { | ||
let AbstractType::Array(arr_element_type) = arr_type else { | ||
let arr_type_name = arr_type.to_string(linker_types); | ||
errors.error(span, format!("Typing Error: Attempting to index into this, but it is not of array type, instead found a {arr_type_name}")); | ||
return &ERROR_TYPE; | ||
}; | ||
&arr_element_type.deref() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,151 @@ | ||
|
||
use std::ops::{Deref, Index}; | ||
|
||
use crate::{abstract_type::AbstractType, errors::ErrorCollector, file_position::Span, flattening::{BinaryOperator, UnaryOperator}, linker::{get_builtin_type, Linkable, NamedType, TypeUUID}, value::Value}; | ||
|
||
pub const BOOL_CONCRETE_TYPE : ConcreteType = ConcreteType::Named(get_builtin_type("bool")); | ||
pub const INT_CONCRETE_TYPE : ConcreteType = ConcreteType::Named(get_builtin_type("int")); | ||
|
||
#[derive(Debug,Clone,PartialEq,Eq)] | ||
pub enum ConcreteType { | ||
Named(TypeUUID), | ||
Value(Value), | ||
Array(Box<(ConcreteType, ConcreteType)>), | ||
Unknown, | ||
Error | ||
} | ||
|
||
impl Into<AbstractType> for &ConcreteType { | ||
fn into(self) -> AbstractType { | ||
match self { | ||
ConcreteType::Named(name) => { | ||
AbstractType::Named(*name) | ||
} | ||
ConcreteType::Value(_) => { | ||
unreachable!("Turning a ConcreteType::Value into an AbstractType"); | ||
} | ||
ConcreteType::Array(arr) => { | ||
let (sub, _sz) = arr.deref(); | ||
let concrete_sub : AbstractType = sub.into(); | ||
AbstractType::Array(Box::new(concrete_sub)) | ||
} | ||
ConcreteType::Unknown => AbstractType::Unknown, | ||
ConcreteType::Error => AbstractType::Error | ||
} | ||
} | ||
} | ||
|
||
|
||
/// Panics on Type Errors that should have been caught by [AbstractType] | ||
/// | ||
/// TODO Add checks for array sizes being equal etc. | ||
pub fn get_unary_operator_expected_output(op : UnaryOperator, input_typ : &ConcreteType) -> ConcreteType { | ||
let gather_type = match op { | ||
UnaryOperator::Not => { | ||
assert_eq!(*input_typ, BOOL_CONCRETE_TYPE); | ||
return BOOL_CONCRETE_TYPE | ||
} | ||
UnaryOperator::Negate => { | ||
assert_eq!(*input_typ, INT_CONCRETE_TYPE); | ||
return INT_CONCRETE_TYPE | ||
} | ||
UnaryOperator::And => BOOL_CONCRETE_TYPE, | ||
UnaryOperator::Or => BOOL_CONCRETE_TYPE, | ||
UnaryOperator::Xor => BOOL_CONCRETE_TYPE, | ||
UnaryOperator::Sum => INT_CONCRETE_TYPE, | ||
UnaryOperator::Product => INT_CONCRETE_TYPE | ||
}; | ||
assert_eq!(input_typ.down_array(), &gather_type); | ||
gather_type | ||
} | ||
|
||
/// Panics on Type Errors that should have been caught by [AbstractType] | ||
/// | ||
/// TODO Add checks for array sizes being equal etc. | ||
pub fn get_binary_operator_expected_output(op : BinaryOperator, left_typ : &ConcreteType, right_typ : &ConcreteType) -> ConcreteType { | ||
let ((in_left, in_right), out) = match op { | ||
BinaryOperator::And => ((BOOL_CONCRETE_TYPE, BOOL_CONCRETE_TYPE), BOOL_CONCRETE_TYPE), | ||
BinaryOperator::Or => ((BOOL_CONCRETE_TYPE, BOOL_CONCRETE_TYPE), BOOL_CONCRETE_TYPE), | ||
BinaryOperator::Xor => ((BOOL_CONCRETE_TYPE, BOOL_CONCRETE_TYPE), BOOL_CONCRETE_TYPE), | ||
BinaryOperator::Add => ((INT_CONCRETE_TYPE, INT_CONCRETE_TYPE), INT_CONCRETE_TYPE), | ||
BinaryOperator::Subtract => ((INT_CONCRETE_TYPE, INT_CONCRETE_TYPE), INT_CONCRETE_TYPE), | ||
BinaryOperator::Multiply => ((INT_CONCRETE_TYPE, INT_CONCRETE_TYPE), INT_CONCRETE_TYPE), | ||
BinaryOperator::Divide => ((INT_CONCRETE_TYPE, INT_CONCRETE_TYPE), INT_CONCRETE_TYPE), | ||
BinaryOperator::Modulo => ((INT_CONCRETE_TYPE, INT_CONCRETE_TYPE), INT_CONCRETE_TYPE), | ||
BinaryOperator::Equals => ((INT_CONCRETE_TYPE, INT_CONCRETE_TYPE), BOOL_CONCRETE_TYPE), | ||
BinaryOperator::NotEquals => ((INT_CONCRETE_TYPE, INT_CONCRETE_TYPE), BOOL_CONCRETE_TYPE), | ||
BinaryOperator::GreaterEq => ((INT_CONCRETE_TYPE, INT_CONCRETE_TYPE), BOOL_CONCRETE_TYPE), | ||
BinaryOperator::Greater => ((INT_CONCRETE_TYPE, INT_CONCRETE_TYPE), BOOL_CONCRETE_TYPE), | ||
BinaryOperator::LesserEq => ((INT_CONCRETE_TYPE, INT_CONCRETE_TYPE), BOOL_CONCRETE_TYPE), | ||
BinaryOperator::Lesser => ((INT_CONCRETE_TYPE, INT_CONCRETE_TYPE), BOOL_CONCRETE_TYPE), | ||
}; | ||
|
||
assert_eq!(*left_typ, in_left); | ||
assert_eq!(*right_typ, in_right); | ||
|
||
out | ||
} | ||
|
||
impl ConcreteType { | ||
#[track_caller] | ||
pub fn unwrap_value(&self) -> &Value { | ||
let ConcreteType::Value(v) = self else {unreachable!("unwrap_value")}; | ||
v | ||
} | ||
pub fn to_string<TypVec : Index<TypeUUID, Output = NamedType>>(&self, linker_types : &TypVec) -> String { | ||
match self { | ||
ConcreteType::Named(name) => linker_types[*name].get_full_name(), | ||
ConcreteType::Array(arr_box) => { | ||
let (elem_typ, arr_size) = arr_box.deref(); | ||
format!("{}[{}]", elem_typ.to_string(linker_types), arr_size.unwrap_value().unwrap_integer()) | ||
} | ||
ConcreteType::Value(v) => format!("{{concrete_type_{v}}}"), | ||
ConcreteType::Unknown => format!("{{concrete_type_unknown}}"), | ||
ConcreteType::Error => format!("{{concrete_type_error}}"), | ||
} | ||
} | ||
pub fn down_array(&self) -> &ConcreteType { | ||
let ConcreteType::Array(arr_box) = self else {unreachable!("Must be an array!")}; | ||
let (sub, _sz) = arr_box.deref(); | ||
sub | ||
} | ||
|
||
pub fn type_compare(&self, found : &ConcreteType) -> bool { | ||
match (self, found) { | ||
(ConcreteType::Named(exp), ConcreteType::Named(fnd)) => exp == fnd, | ||
(ConcreteType::Array(exp), ConcreteType::Array(fnd)) => { | ||
let (target_arr_typ, target_arr_size) = exp.deref(); | ||
let (found_arr_typ, found_arr_size) = fnd.deref(); | ||
target_arr_typ.type_compare(found_arr_typ) && target_arr_size.type_compare(found_arr_size) | ||
} | ||
(ConcreteType::Value(lv), ConcreteType::Value(rv)) => lv == rv, | ||
(ConcreteType::Error, _) | (_, ConcreteType::Error) => true, // Just assume correct, because the other side has an error | ||
(ConcreteType::Unknown, _) | (_, ConcreteType::Unknown) => todo!("Type Unification {self:?} {found:?}"), | ||
_ => false, | ||
} | ||
} | ||
pub fn check_type<TypVec : Index<TypeUUID, Output = NamedType>>(&self, source_type : &ConcreteType, span : Span, linker_types : &TypVec, errors : &ErrorCollector) { | ||
if !self.type_compare(source_type) { | ||
errors.error(span, format!("Concrete Type Error! Expected {} but found {}", self.to_string(linker_types), source_type.to_string(linker_types))); | ||
} | ||
} | ||
|
||
pub fn check_or_update_type<TypVec : Index<TypeUUID, Output = NamedType>>(&mut self, source_type : &ConcreteType, span : Span, linker_types : &TypVec, errors : &ErrorCollector) { | ||
if *self == ConcreteType::Unknown { | ||
*self = source_type.clone(); | ||
} else { | ||
self.check_type(source_type, span, linker_types, errors); | ||
} | ||
} | ||
|
||
pub fn typecheck_concrete_unary_operator<TypVec : Index<TypeUUID, Output = NamedType>>(&mut self, op : UnaryOperator, input_typ : &ConcreteType, span : Span, linker_types : &TypVec, errors : &ErrorCollector) { | ||
let expected = get_unary_operator_expected_output(op, input_typ); | ||
|
||
self.check_or_update_type(&expected, span, linker_types, errors); | ||
} | ||
pub fn typecheck_concrete_binary_operator<TypVec : Index<TypeUUID, Output = NamedType>>(&mut self, op : BinaryOperator, left_typ : &ConcreteType, right_typ : &ConcreteType, span : Span, linker_types : &TypVec, errors : &ErrorCollector) { | ||
let expected = get_binary_operator_expected_output(op, left_typ, right_typ); | ||
|
||
self.check_or_update_type(&expected, span, linker_types, errors); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.