finished revamping type system as setup for generics

This commit is contained in:
Andrew Segavac
2021-10-24 15:59:26 -06:00
parent fbb81f3d09
commit 742c271732
7 changed files with 529 additions and 352 deletions

View File

@@ -17,7 +17,7 @@ impl IdGenerator {
pub fn new_unit() -> TypeUsage {
TypeUsage::Named(NamedTypeUsage {
type_parameters: GenericUsage::Known(GenericInstantiation{parameters: vec!()}),
type_parameters: GenericUsage::Known(GenericInstantiation { parameters: vec![] }),
name: Identifier {
name: Spanned {
span: Span { left: 0, right: 0 }, //todo: figure out a sane value for these
@@ -29,7 +29,7 @@ pub fn new_unit() -> TypeUsage {
pub fn new_never() -> TypeUsage {
TypeUsage::Named(NamedTypeUsage {
type_parameters: GenericUsage::Known(GenericInstantiation{parameters: vec!()}),
type_parameters: GenericUsage::Known(GenericInstantiation { parameters: vec![] }),
name: Identifier {
name: Spanned {
span: Span { left: 0, right: 0 }, //todo: figure out a sane value for these
@@ -81,12 +81,15 @@ impl TypeUsage {
}
pub fn new_named(identifier: &Identifier, generic_usage: &GenericUsage) -> TypeUsage {
return TypeUsage::Named(NamedTypeUsage { type_parameters: generic_usage.clone(), name: identifier.clone() });
return TypeUsage::Named(NamedTypeUsage {
type_parameters: generic_usage.clone(),
name: identifier.clone(),
});
}
pub fn new_builtin(name: String) -> TypeUsage {
TypeUsage::Named(NamedTypeUsage {
type_parameters: GenericUsage::Known(GenericInstantiation{parameters: vec!()}),
type_parameters: GenericUsage::Known(GenericInstantiation { parameters: vec![] }),
name: Identifier {
name: Spanned {
span: Span { left: 0, right: 0 }, //todo: figure out a sane value for these
@@ -128,16 +131,12 @@ pub enum GenericUsage {
impl GenericUsage {
pub fn new(type_parameters: &[TypeUsage]) -> Self {
GenericUsage::Known(GenericInstantiation{
parameters: type_parameters.iter().map(|tp| {
tp.clone()
}).collect(),
GenericUsage::Known(GenericInstantiation {
parameters: type_parameters.iter().map(|tp| tp.clone()).collect(),
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum Operator {
Mul,

View File

@@ -27,10 +27,7 @@ pub enum TypingError {
#[error("name is not a struct, cannot instaniate")]
NotAStructLiteral { identifier: ast::Identifier },
#[error("struct literal fields mismatch")]
StructLiteralFieldsMismatch {
struct_name: ast::Identifier,
struct_definition_name: ast::Identifier,
},
StructLiteralFieldsMismatch { struct_name: ast::Identifier },
#[error("missing trait function")]
MissingTraitFunction {
struct_name: ast::Identifier,
@@ -48,6 +45,8 @@ pub enum TypingError {
IfConditionMustBeBool {
// TODO: add position
},
#[error("cannot use type as an expression")]
TypeIsNotAnExpression { type_name: ast::Identifier },
#[error("multiple errors")]
MultipleErrors { errors: Vec<TypingError> },
}

View File

@@ -95,12 +95,10 @@ impl Context {
ast::ModuleItem::Function(function) => {
ctx.environment.insert(
function.declaration.name.name.value.to_string(),
NamedEntity::Variable(Value::Function(
Function{
partial: vec!(),
ref_: FunctionRef::User(function.clone()),
}
)),
NamedEntity::Variable(Value::Function(Function {
partial: vec![],
ref_: FunctionRef::User(function.clone()),
})),
);
}
ast::ModuleItem::Impl(impl_) => {
@@ -225,11 +223,9 @@ impl TreeWalkInterpreter {
let mut ctx = Context::from_module(module);
let main = match &ctx.environment["main"] {
NamedEntity::Variable(Value::Function(func)) => {
match &func.ref_ {
FunctionRef::User(ref_) => ref_.clone(),
_ => panic!("main should be a user defined function"),
}
NamedEntity::Variable(Value::Function(func)) => match &func.ref_ {
FunctionRef::User(ref_) => ref_.clone(),
_ => panic!("main should be a user defined function"),
},
_ => panic!("main should be a user defined function"),
};
@@ -378,26 +374,35 @@ impl TreeWalkInterpreter {
argument_values.push(argument_value);
}
match &source {
Value::Function(function) => {
match &function.ref_ {
FunctionRef::User(user_function) => {
let mut fn_ctx = ctx.new_env();
let mut i = 0;
for partial_arg in &function.partial {
fn_ctx.set_variable(user_function.declaration.arguments[i].name.name.value.to_string(), &partial_arg.clone());
i = i + 1;
}
for argument_value in &argument_values {
fn_ctx.set_variable(user_function.declaration.arguments[i].name.name.value.to_string(), &argument_value.clone());
}
return ExpressionResult::Value(self.with_function(&mut fn_ctx, user_function));
Value::Function(function) => match &function.ref_ {
FunctionRef::User(user_function) => {
let mut fn_ctx = ctx.new_env();
let mut i = 0;
for partial_arg in &function.partial {
fn_ctx.set_variable(
user_function.declaration.arguments[i].name.name.value.to_string(),
&partial_arg.clone(),
);
i = i + 1;
}
FunctionRef::Builtin(builtin_function) => {
let all_values = function.partial.iter().map(|val| {val.clone()}).chain(argument_values.into_iter()).collect();
return ExpressionResult::Value(builtin_function(all_values));
for argument_value in &argument_values {
fn_ctx.set_variable(
user_function.declaration.arguments[i].name.name.value.to_string(),
&argument_value.clone(),
);
}
return ExpressionResult::Value(self.with_function(&mut fn_ctx, user_function));
}
}
FunctionRef::Builtin(builtin_function) => {
let all_values = function
.partial
.iter()
.map(|val| val.clone())
.chain(argument_values.into_iter())
.collect();
return ExpressionResult::Value(builtin_function(all_values));
}
},
_ => panic!("type error: function call source must be a function"),
}
}
@@ -454,17 +459,17 @@ impl TreeWalkInterpreter {
match &method.declaration.arguments[0].type_ {
ast::TypeUsage::Named(arg_named) => {
if arg_named.name.name.value == s.source.name.name.value {
return ExpressionResult::Value(Value::Function(Function{
partial: vec!(source.clone()),
return ExpressionResult::Value(Value::Function(Function {
partial: vec![source.clone()],
ref_: FunctionRef::User(method.clone()),
}));
}
}
_ => {},
_ => {}
}
}
return ExpressionResult::Value(Value::Function(Function{
partial: vec!(),
return ExpressionResult::Value(Value::Function(Function {
partial: vec![],
ref_: FunctionRef::User(method.clone()),
}));
}

View File

@@ -2,6 +2,7 @@
mod ast;
mod errors;
mod interpreter;
mod trait_checking;
mod type_alias_resolution;
mod type_checking;
#[macro_use]
@@ -42,6 +43,14 @@ fn main() {
let alias_resolver = type_alias_resolution::TypeAliasResolver {};
let resolved_ast = alias_resolver.with_module(&module_ast);
// println!("resolved ast: {:#?}", &resolved_ast);
let trait_checker = trait_checking::TraitChecker {};
match trait_checker.with_module(&resolved_ast) {
Ok(_) => {}
Err(err) => {
println!("type checking error: {:#?}", &err);
return;
}
}
let type_checker = type_checking::TypeChecker {};
let type_checking_result = type_checker.with_module(&resolved_ast);
match &type_checking_result {

171
src/trait_checking.rs Normal file
View File

@@ -0,0 +1,171 @@
use crate::ast;
use crate::errors;
use std::collections::HashMap;
pub type Result<T, E = errors::TypingError> = std::result::Result<T, E>;
#[derive(Debug, Clone, PartialEq, Eq)]
struct Context {
pub environment_traits: HashMap<String, ast::TraitTypeDeclaration>,
}
fn create_builtins() -> HashMap<String, ast::TraitTypeDeclaration> {
let mut result = HashMap::<String, ast::TraitTypeDeclaration>::new();
return result;
}
fn compare_struct_trait(
struct_: &ast::TypeUsage,
trait_: &ast::TypeUsage,
struct_name: &ast::Identifier,
trait_name: &ast::Identifier,
) -> Result<()> {
match struct_ {
ast::TypeUsage::Named(named) => match trait_ {
ast::TypeUsage::Named(trait_named) => {
if named.name.name.value == trait_named.name.name.value
|| (named.name.name.value == struct_name.name.value && trait_named.name.name.value == trait_name.name.value)
{
return Ok(());
}
return Err(errors::TypingError::TypeMismatch {
type_one: struct_.clone(),
type_two: trait_.clone(),
});
}
ast::TypeUsage::Function(_) => {
return Err(errors::TypingError::TypeMismatch {
type_one: struct_.clone(),
type_two: trait_.clone(),
});
}
_ => panic!("Unknown in function definition"),
},
ast::TypeUsage::Function(function) => match trait_ {
ast::TypeUsage::Named(_) => {
return Err(errors::TypingError::TypeMismatch {
type_one: struct_.clone(),
type_two: trait_.clone(),
});
}
ast::TypeUsage::Function(trait_function) => {
if function.arguments.len() != trait_function.arguments.len() {
return Err(errors::TypingError::TypeMismatch {
type_one: struct_.clone(),
type_two: trait_.clone(),
});
}
for (i, _) in function.arguments.iter().enumerate() {
compare_struct_trait(&function.arguments[i], &trait_function.arguments[i], struct_name, trait_name)?;
}
compare_struct_trait(&function.return_type, &trait_function.return_type, struct_name, trait_name)?;
return Ok(());
}
_ => panic!("Unknown in function definition"),
},
_ => panic!("Unknown in function definition"),
}
}
pub struct TraitChecker {}
impl TraitChecker {
pub fn with_module(self: &Self, module: &ast::Module) -> Result<()> {
let mut ctx = Context {
environment_traits: create_builtins(),
};
for item in module.items.iter() {
match item {
ast::ModuleItem::TypeDeclaration(ast::TypeDeclaration::Trait(trait_)) => {
ctx.environment_traits.insert(trait_.name.name.value.to_string(), trait_.clone());
}
_ => {}
}
}
for item in module.items.iter() {
match item {
ast::ModuleItem::Impl(impl_) => {
self.with_impl(&ctx, impl_)?;
}
_ => {}
}
}
return Ok(());
}
fn with_impl(self: &Self, ctx: &Context, impl_: &ast::Impl) -> Result<()> {
// See if trait actually matches
match &impl_.trait_ {
Some(trait_) => {
// assert trait functions satisfied
if !ctx.environment_traits.contains_key(&trait_.name.value) {
return Err(errors::TypingError::TypeDoesNotExist {
identifier: trait_.clone(),
});
}
let trait_declaration = &ctx.environment_traits[&trait_.name.value];
for trait_item in trait_declaration.functions.iter() {
match trait_item {
ast::TraitItem::FunctionDeclaration(declaration) => {
let mut found = false;
for impl_function in impl_.functions.iter() {
if impl_function.declaration.name.name.value == declaration.name.name.value {
found = true;
compare_struct_trait(
&impl_function.declaration.to_type(),
&declaration.to_type(),
&impl_.struct_name,
&trait_,
)?;
}
}
if found == false {
return Err(errors::TypingError::MissingTraitFunction {
struct_name: impl_.struct_name.clone(),
function_name: declaration.name.clone(),
});
}
}
ast::TraitItem::Function(function) => {
// skip found check because it has a default
for impl_function in impl_.functions.iter() {
if impl_function.declaration.name.name.value == function.declaration.name.name.value {
compare_struct_trait(
&impl_function.declaration.to_type(),
&function.declaration.to_type(),
&impl_.struct_name,
&trait_,
)?;
}
}
}
}
}
// assert all functions are in trait
for impl_function in impl_.functions.iter() {
let mut found = false;
for trait_item in trait_declaration.functions.iter() {
let declaration = match trait_item {
ast::TraitItem::Function(function) => &function.declaration,
ast::TraitItem::FunctionDeclaration(declaration) => &declaration,
};
if impl_function.declaration.name.name.value == declaration.name.name.value {
found = true;
break;
}
}
if found == false {
return Err(errors::TypingError::FunctionNotInTrait {
function_name: impl_function.declaration.name.clone(),
});
}
}
}
None => {}
}
// TODO: check for duplicate functions
return Ok(());
}
}

View File

@@ -138,7 +138,7 @@ impl TypeAliasResolver {
},
replaces: ast::TypeUsage::Named(ast::NamedTypeUsage {
type_parameters: ast::GenericUsage::Unknown,
name: trait_.name.clone()
name: trait_.name.clone(),
}),
});
return ast::TraitTypeDeclaration {
@@ -148,9 +148,7 @@ impl TypeAliasResolver {
.functions
.iter()
.map(|f| match f {
ast::TraitItem::Function(function) => {
ast::TraitItem::Function(self.with_function(&trait_ctx, function))
},
ast::TraitItem::Function(function) => ast::TraitItem::Function(self.with_function(&trait_ctx, function)),
ast::TraitItem::FunctionDeclaration(function_declaration) => {
ast::TraitItem::FunctionDeclaration(self.with_function_declaration(&trait_ctx, function_declaration))
}

View File

@@ -6,16 +6,72 @@ pub type SubstitutionMap = HashMap<String, ast::TypeUsage>;
pub type Result<T, E = errors::TypingError> = std::result::Result<T, E>;
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EnvImpl {
trait_: Option<String>,
functions: HashMap<String, ast::TypeUsage>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum TypeType {
Scalar,
Trait,
Struct,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct EnvType {
is_a: TypeType,
fields: HashMap<String, ast::TypeUsage>,
impls: Vec<EnvImpl>,
}
impl EnvType {
fn from_struct(struct_: &ast::StructTypeDeclaration) -> EnvType {
return EnvType {
is_a: TypeType::Struct,
fields: struct_
.fields
.iter()
.map(|field| (field.name.name.value.to_string(), field.type_.clone()))
.collect(),
impls: vec![],
};
}
fn from_trait(trait_: &ast::TraitTypeDeclaration) -> EnvType {
let mut functions = HashMap::new();
for func in trait_.functions.iter() {
match func {
ast::TraitItem::FunctionDeclaration(fd) => {
functions.insert(fd.name.name.value.to_string(), fd.to_type());
}
ast::TraitItem::Function(f) => {
functions.insert(f.declaration.name.name.value.to_string(), f.declaration.to_type());
}
}
}
let impl_ = EnvImpl {
trait_: None,
functions: functions,
};
return EnvType {
is_a: TypeType::Trait,
fields: HashMap::new(),
impls: vec![impl_],
};
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum NamedEntity {
TypeDeclaration(ast::TypeDeclaration),
NamedType(EnvType),
Variable(ast::TypeUsage),
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct Context {
pub current_function_return: Option<ast::TypeUsage>,
pub impls: Vec<ast::Impl>,
pub environment: HashMap<String, NamedEntity>,
}
@@ -23,99 +79,127 @@ fn create_builtins() -> HashMap<String, NamedEntity> {
let mut result = HashMap::new();
result.insert(
"i8".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration {
name: "i8".to_string(),
})),
NamedEntity::NamedType(EnvType {
is_a: TypeType::Scalar,
fields: HashMap::new(),
impls: vec![],
}),
);
result.insert(
"i16".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration {
name: "i16".to_string(),
})),
NamedEntity::NamedType(EnvType {
is_a: TypeType::Scalar,
fields: HashMap::new(),
impls: vec![],
}),
);
result.insert(
"i32".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration {
name: "i32".to_string(),
})),
NamedEntity::NamedType(EnvType {
is_a: TypeType::Scalar,
fields: HashMap::new(),
impls: vec![],
}),
);
result.insert(
"i64".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration {
name: "i64".to_string(),
})),
NamedEntity::NamedType(EnvType {
is_a: TypeType::Scalar,
fields: HashMap::new(),
impls: vec![],
}),
);
result.insert(
"isize".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration {
name: "isize".to_string(),
})),
NamedEntity::NamedType(EnvType {
is_a: TypeType::Scalar,
fields: HashMap::new(),
impls: vec![],
}),
);
result.insert(
"u8".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration {
name: "u8".to_string(),
})),
NamedEntity::NamedType(EnvType {
is_a: TypeType::Scalar,
fields: HashMap::new(),
impls: vec![],
}),
);
result.insert(
"u16".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration {
name: "u16".to_string(),
})),
NamedEntity::NamedType(EnvType {
is_a: TypeType::Scalar,
fields: HashMap::new(),
impls: vec![],
}),
);
result.insert(
"u32".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration {
name: "u32".to_string(),
})),
NamedEntity::NamedType(EnvType {
is_a: TypeType::Scalar,
fields: HashMap::new(),
impls: vec![],
}),
);
result.insert(
"u64".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration {
name: "u64".to_string(),
})),
NamedEntity::NamedType(EnvType {
is_a: TypeType::Scalar,
fields: HashMap::new(),
impls: vec![],
}),
);
result.insert(
"usize".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration {
name: "usize".to_string(),
})),
NamedEntity::NamedType(EnvType {
is_a: TypeType::Scalar,
fields: HashMap::new(),
impls: vec![],
}),
);
result.insert(
"f32".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration {
name: "f32".to_string(),
})),
NamedEntity::NamedType(EnvType {
is_a: TypeType::Scalar,
fields: HashMap::new(),
impls: vec![],
}),
);
result.insert(
"f64".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration {
name: "f64".to_string(),
})),
NamedEntity::NamedType(EnvType {
is_a: TypeType::Scalar,
fields: HashMap::new(),
impls: vec![],
}),
);
result.insert(
"bool".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration {
name: "bool".to_string(),
})),
NamedEntity::NamedType(EnvType {
is_a: TypeType::Scalar,
fields: HashMap::new(),
impls: vec![],
}),
);
result.insert(
"!".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration {
name: "!".to_string(),
})),
NamedEntity::NamedType(EnvType {
is_a: TypeType::Scalar,
fields: HashMap::new(),
impls: vec![],
}),
);
result.insert(
"unit".to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Primitive(ast::PrimitiveTypeDeclaration {
name: "unit".to_string(),
})),
NamedEntity::NamedType(EnvType {
is_a: TypeType::Scalar,
fields: HashMap::new(),
impls: vec![],
}),
);
return result;
}
@@ -124,167 +208,67 @@ enum StructAttr {
Method(ast::TypeUsage),
}
fn get_struct_attr(ctx: &Context, struct_declaration: &ast::StructTypeDeclaration, attribute: &ast::Identifier) -> Result<StructAttr> {
for field in struct_declaration.fields.iter() {
if field.name.name.value == attribute.name.value {
return Ok(StructAttr::Field(field.type_.clone()));
}
}
let mut result = Vec::new();
for impl_ in ctx.impls.iter() {
if &struct_declaration.name.name.value != &impl_.struct_name.name.value {
continue;
}
for method in impl_.functions.iter() {
if method.declaration.name.name.value == attribute.name.value {
let mut function_type = method.declaration.to_type();
// if the name of the type of the first argument == the class, remove the first arg
if method.declaration.arguments.len() > 0 {
match &method.declaration.arguments[0].type_ {
ast::TypeUsage::Named(named) => {
if named.name.name.value == struct_declaration.name.name.value {
function_type = method.declaration.to_method_type();
}
}
_ => {}
};
}
result.push(function_type);
}
}
}
// TODO: default trait impls
if result.len() == 0 {
return Err(errors::TypingError::UnknownFieldName {
identifier: attribute.clone(),
});
}
if result.len() > 1 {
return Err(errors::TypingError::MultipleFieldName {
identifier: attribute.clone(),
});
}
return Ok(StructAttr::Method(result[0].clone()));
}
fn get_trait_attr(ctx: &Context, trait_declaration: &ast::TraitTypeDeclaration, attribute: &ast::Identifier) -> Result<StructAttr> {
let mut result = Vec::new();
for trait_item in trait_declaration.functions.iter() {
let declaration = match trait_item {
ast::TraitItem::Function(function) => &function.declaration,
ast::TraitItem::FunctionDeclaration(declaration) => declaration,
};
if declaration.name.name.value == attribute.name.value {
let mut function_type = declaration.to_type();
println!("foo: {:?}", declaration);
// if the name of the type of the first argument == the class, remove the first arg
if declaration.arguments.len() > 0 {
match &declaration.arguments[0].type_ {
fn apply_self(type_name: &str, type_: &ast::TypeUsage) -> ast::TypeUsage {
match type_ {
ast::TypeUsage::Function(func) => {
if func.arguments.len() > 0 {
match &func.arguments[0] {
ast::TypeUsage::Named(named) => {
if named.name.name.value == trait_declaration.name.name.value {
function_type = declaration.to_method_type();
if type_name == named.name.name.value {
return ast::TypeUsage::Function(ast::FunctionTypeUsage {
arguments: func.arguments[1..func.arguments.len()].iter().map(|arg| arg.clone()).collect(),
return_type: func.return_type.clone(),
});
}
}
_ => {}
}
}
}
_ => {}
}
return type_.clone();
}
fn get_attr(ctx: &Context, get_from: &NamedEntity, attribute: &ast::Identifier) -> Result<StructAttr> {
match get_from {
NamedEntity::NamedType(env_type) => {
if env_type.fields.contains_key(&attribute.name.value) {
return Ok(StructAttr::Field(env_type.fields[&attribute.name.value].clone()));
}
let mut result = Vec::new();
for impl_ in env_type.impls.iter() {
if impl_.functions.contains_key(&attribute.name.value) {
result.push(impl_.functions[&attribute.name.value].clone())
}
}
if result.len() == 0 {
return Err(errors::TypingError::UnknownFieldName {
identifier: attribute.clone(),
});
}
if result.len() > 1 {
return Err(errors::TypingError::MultipleFieldName {
identifier: attribute.clone(),
});
}
return Ok(StructAttr::Method(result[0].clone()));
}
NamedEntity::Variable(type_) => match type_ {
ast::TypeUsage::Named(named) => {
let attr = get_attr(ctx, &ctx.environment[&named.name.name.value], attribute)?;
let method = match attr {
StructAttr::Field(field) => return Ok(StructAttr::Field(field)),
StructAttr::Method(method) => method,
};
return Ok(StructAttr::Method(apply_self(&named.name.name.value, &method)));
}
result.push(function_type);
}
}
if result.len() == 0 {
return Err(errors::TypingError::UnknownFieldName {
identifier: attribute.clone(),
});
}
if result.len() > 1 {
return Err(errors::TypingError::MultipleFieldName {
identifier: attribute.clone(),
});
}
return Ok(StructAttr::Method(result[0].clone()));
}
fn get_attr(ctx: &Context, source_type: &ast::TypeUsage, attribute: &ast::Identifier) -> Result<StructAttr> {
match source_type {
ast::TypeUsage::Named(named) => {
match &ctx.environment[&named.name.name.value] {
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Struct(type_declaration)) => {
return get_struct_attr(ctx, type_declaration, attribute);
}
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Trait(type_declaration)) => {
return get_trait_attr(ctx, type_declaration, attribute);
}
_ => {
return Err(errors::TypingError::AttributeOfNonstruct {
identifier: attribute.clone(),
});
// TODO: support builtins - float, int, etc.
}
}
}
ast::TypeUsage::Function(_) => {
return Err(errors::TypingError::NotAStructLiteral {
identifier: attribute.clone(),
});
}
_ => {
panic!("tried to get attr of unknown");
}
};
}
fn compare_struct_trait(
struct_: &ast::TypeUsage,
trait_: &ast::TypeUsage,
struct_name: &ast::Identifier,
trait_name: &ast::Identifier,
) -> Result<()> {
match struct_ {
ast::TypeUsage::Named(named) => match trait_ {
ast::TypeUsage::Named(trait_named) => {
if named.name.name.value == trait_named.name.name.value
|| (named.name.name.value == struct_name.name.value && trait_named.name.name.value == trait_name.name.value)
{
return Ok(());
}
return Err(errors::TypingError::TypeMismatch {
type_one: struct_.clone(),
type_two: trait_.clone(),
_ => {
return Err(errors::TypingError::AttributeOfNonstruct {
identifier: attribute.clone(),
});
}
ast::TypeUsage::Function(_) => {
return Err(errors::TypingError::TypeMismatch {
type_one: struct_.clone(),
type_two: trait_.clone(),
});
}
_ => panic!("Unknown in function definition"),
},
ast::TypeUsage::Function(function) => match trait_ {
ast::TypeUsage::Named(_) => {
return Err(errors::TypingError::TypeMismatch {
type_one: struct_.clone(),
type_two: trait_.clone(),
});
}
ast::TypeUsage::Function(trait_function) => {
if function.arguments.len() != trait_function.arguments.len() {
return Err(errors::TypingError::TypeMismatch {
type_one: struct_.clone(),
type_two: trait_.clone(),
});
}
for (i, _) in function.arguments.iter().enumerate() {
compare_struct_trait(&function.arguments[i], &trait_function.arguments[i], struct_name, trait_name)?;
}
compare_struct_trait(&function.return_type, &trait_function.return_type, struct_name, trait_name)?;
return Ok(());
}
_ => panic!("Unknown in function definition"),
},
_ => panic!("Unknown in function definition"),
}
}
@@ -300,6 +284,59 @@ impl Context {
ctx.current_function_return = Some(function.clone());
return ctx;
}
fn add_impl(&self, impl_: &ast::Impl, traits: &HashMap<String, ast::TraitTypeDeclaration>) -> Result<Context> {
let mut functions = HashMap::new();
for func in impl_.functions.iter() {
functions.insert(func.declaration.name.name.value.to_string(), func.declaration.to_type());
}
// fill out defaults
match &impl_.trait_ {
Some(trait_name) => {
if !traits.contains_key(&trait_name.name.value) {
return Err(errors::TypingError::TypeDoesNotExist {
identifier: trait_name.clone(),
});
}
for func in traits[&trait_name.name.value].functions.iter() {
match func {
ast::TraitItem::Function(default_function) => {
if !functions.contains_key(&default_function.declaration.name.name.value) {
functions.insert(
default_function.declaration.name.name.value.to_string(),
default_function.declaration.to_type(),
);
}
}
_ => {}
}
}
}
None => {}
}
let mut result = self.clone();
let mut env_named = result.environment[&impl_.struct_name.name.value].clone();
match &mut env_named {
NamedEntity::NamedType(env_type) => {
env_type.impls.push(EnvImpl {
trait_: match &impl_.trait_ {
Some(trait_) => Some(trait_.name.value.to_string()),
None => None,
},
functions: functions,
});
result
.environment
.insert(impl_.struct_name.name.value.to_string(), NamedEntity::NamedType(env_type.clone()));
}
NamedEntity::Variable(_) => {
return Err(errors::TypingError::TypeDoesNotExist {
identifier: impl_.struct_name.clone(),
});
}
}
return Ok(result);
}
}
fn type_exists(ctx: &Context, type_: &ast::TypeUsage) -> Result<()> {
@@ -311,7 +348,7 @@ fn type_exists(ctx: &Context, type_: &ast::TypeUsage) -> Result<()> {
});
}
match ctx.environment[&named.name.name.value] {
NamedEntity::TypeDeclaration(_) => {
NamedEntity::NamedType(_) => {
// is a type
}
_ => {
@@ -464,28 +501,24 @@ impl TypeChecker {
pub fn with_module(self: &Self, module: &ast::Module) -> Result<(ast::Module, SubstitutionMap)> {
let mut ctx = Context {
environment: create_builtins(),
impls: Vec::new(),
current_function_return: None,
};
let mut traits = HashMap::new();
for item in module.items.iter() {
match item {
ast::ModuleItem::TypeDeclaration(ast::TypeDeclaration::Struct(struct_)) => {
ctx.environment.insert(
struct_.name.name.value.to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Struct(struct_.clone())),
);
}
ast::ModuleItem::TypeDeclaration(ast::TypeDeclaration::Alias(alias)) => {
ctx.environment.insert(
alias.name.name.value.to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Alias(alias.clone())),
NamedEntity::NamedType(EnvType::from_struct(&struct_)),
);
}
ast::ModuleItem::TypeDeclaration(ast::TypeDeclaration::Trait(trait_)) => {
traits.insert(trait_.name.name.value.to_string(), trait_.clone());
ctx.environment.insert(
trait_.name.name.value.to_string(),
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Trait(trait_.clone())),
NamedEntity::NamedType(EnvType::from_trait(&trait_)),
);
}
ast::ModuleItem::Function(function) => {
@@ -498,8 +531,19 @@ impl TypeChecker {
NamedEntity::Variable(ast::TypeUsage::Function(function_type)),
);
}
_ => {}
}
}
for item in module.items.iter() {
match item {
ast::ModuleItem::Impl(impl_) => {
ctx.impls.push(impl_.clone());
if !ctx.environment.contains_key(&impl_.struct_name.name.value) {
return Err(errors::TypingError::IdentifierIsNotType {
identifier: impl_.struct_name.clone(),
});
}
ctx = ctx.add_impl(&impl_, &traits)?;
}
_ => {}
}
@@ -530,11 +574,7 @@ impl TypeChecker {
return Ok((result, subst));
}
fn with_function_declaration(
self: &Self,
ctx: &Context,
declaration: &ast::FunctionDeclaration,
) -> Result<ast::FunctionDeclaration> {
fn with_function_declaration(self: &Self, ctx: &Context, declaration: &ast::FunctionDeclaration) -> Result<ast::FunctionDeclaration> {
for arg in declaration.arguments.iter() {
type_exists(ctx, &arg.type_)?;
}
@@ -621,7 +661,7 @@ impl TypeChecker {
trait_: &ast::TraitTypeDeclaration,
) -> Result<(ast::TraitTypeDeclaration, SubstitutionMap)> {
let mut substitutions = incoming_substitutions.clone();
let mut result_functions = vec!();
let mut result_functions = vec![];
for item in &trait_.functions {
match item {
ast::TraitItem::FunctionDeclaration(declaration) => {
@@ -668,79 +708,16 @@ impl TypeChecker {
impl_: &ast::Impl,
) -> Result<(ast::Impl, SubstitutionMap)> {
let mut substitutions = incoming_substitutions.clone();
type_exists(ctx, &ast::TypeUsage::new_named(&impl_.struct_name.clone(), &ast::GenericUsage::Unknown))?;
type_exists(
ctx,
&ast::TypeUsage::new_named(&impl_.struct_name.clone(), &ast::GenericUsage::Unknown),
)?;
let mut functions = vec![];
for function in impl_.functions.iter() {
let (result, function_subs) = self.with_function(&ctx, &substitutions, function)?;
substitutions = compose_substitutions(ctx, &substitutions, &function_subs)?;
functions.push(result);
}
// See if trait actually matches
match &impl_.trait_ {
Some(trait_) => {
// assert trait functions satisfied
if !ctx.environment.contains_key(&trait_.name.value) {
return Err(errors::TypingError::TypeDoesNotExist {
identifier: trait_.clone(),
});
}
let trait_declaration = match &ctx.environment[&trait_.name.value] {
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Trait(declaration)) => declaration,
_ => {
return Err(errors::TypingError::ImplTraitMustBeTrait {
trait_name: trait_.clone(),
});
}
};
for trait_item in trait_declaration.functions.iter() {
match trait_item {
ast::TraitItem::FunctionDeclaration(declaration) => {
let mut found = false;
for impl_function in impl_.functions.iter() {
if impl_function.declaration.name.name.value == declaration.name.name.value {
found = true;
compare_struct_trait(&impl_function.declaration.to_type(), &declaration.to_type(), &impl_.struct_name, &trait_)?;
}
}
if found == false {
return Err(errors::TypingError::MissingTraitFunction {
struct_name: impl_.struct_name.clone(),
function_name: declaration.name.clone(),
});
}
}
ast::TraitItem::Function(function) => {
// skip found check because it has a default
for impl_function in impl_.functions.iter() {
if impl_function.declaration.name.name.value == function.declaration.name.name.value {
compare_struct_trait(&impl_function.declaration.to_type(), &function.declaration.to_type(), &impl_.struct_name, &trait_)?;
}
}
}
}
}
// assert all functions are in trait
for impl_function in impl_.functions.iter() {
let mut found = false;
for trait_item in trait_declaration.functions.iter() {
let declaration = match trait_item {
ast::TraitItem::Function(function) => &function.declaration,
ast::TraitItem::FunctionDeclaration(declaration) => declaration,
};
if impl_function.declaration.name.name.value == declaration.name.name.value {
found = true;
break;
}
}
if found == false {
return Err(errors::TypingError::FunctionNotInTrait {
function_name: impl_function.declaration.name.clone(),
});
}
}
}
None => {}
}
return Ok((
ast::Impl {
generic: impl_.generic.clone(),
@@ -903,7 +880,7 @@ impl TypeChecker {
let (source, subst) = self.with_expression(ctx, &substitution, &struct_attr.source)?;
let mut subst = subst.clone();
let field_type = match get_attr(ctx, &source.type_, &struct_attr.attribute)? {
let field_type = match get_attr(ctx, &NamedEntity::Variable(source.type_.clone()), &struct_attr.attribute)? {
StructAttr::Field(type_) => type_,
StructAttr::Method(_) => {
return Err(errors::TypingError::CannotAssignToMethod {
@@ -1062,40 +1039,47 @@ impl TypeChecker {
literal_struct: &ast::LiteralStruct,
) -> Result<(ast::LiteralStruct, SubstitutionMap)> {
let mut substitution = substitution.clone();
let type_declaration = match &ctx.environment[&literal_struct.name.name.value] {
NamedEntity::TypeDeclaration(ast::TypeDeclaration::Struct(type_declaration)) => type_declaration,
let struct_type = match &ctx.environment[&literal_struct.name.name.value] {
NamedEntity::NamedType(env_type) => match &env_type.is_a {
TypeType::Struct => env_type.clone(),
_ => {
return Err(errors::TypingError::NotAStructLiteral {
identifier: literal_struct.name.clone(),
});
}
},
_ => {
return Err(errors::TypingError::NotAStructLiteral {
identifier: literal_struct.name.clone(),
});
}
};
if type_declaration.fields.len() != literal_struct.fields.len() {
if struct_type.fields.len() != literal_struct.fields.len() {
return Err(errors::TypingError::StructLiteralFieldsMismatch {
struct_name: literal_struct.name.clone(),
struct_definition_name: type_declaration.name.clone(),
});
}
let mut fields = vec![];
for type_field in type_declaration.fields.iter() {
for (type_field_name, type_field_type) in struct_type.fields.iter() {
let mut found = false;
let mut field_expression: Option<ast::Expression> = None;
let mut field_name: Option<ast::Identifier> = None;
for field in literal_struct.fields.iter() {
if type_field.name.name.value == field.0.name.value {
if type_field_name == &field.0.name.value {
found = true;
let (result, subst) = self.with_expression(ctx, &substitution, &field.1)?;
substitution = compose_substitutions(ctx, &substitution, &subst)?;
substitution = compose_substitutions(ctx, &substitution, &unify(ctx, &type_field.type_, &result.type_)?)?;
substitution = compose_substitutions(ctx, &substitution, &unify(ctx, type_field_type, &result.type_)?)?;
field_expression = Some(result);
field_name = Some(field.0.clone());
}
}
if !found {
return Err(errors::TypingError::StructLiteralFieldsMismatch {
struct_name: literal_struct.name.clone(),
struct_definition_name: type_field.name.clone(),
});
}
fields.push((type_field.name.clone(), field_expression.unwrap()));
fields.push((field_name.unwrap(), field_expression.unwrap()));
}
Ok((
ast::LiteralStruct {
@@ -1164,8 +1148,10 @@ impl TypeChecker {
) -> Result<(ast::VariableUsage, SubstitutionMap)> {
let mut substitution = substitution.clone();
match &ctx.environment[&variable_usage.name.name.value] {
NamedEntity::TypeDeclaration(_) => {
panic!("Using types not yet supported");
NamedEntity::NamedType(_) => {
return Err(errors::TypingError::TypeIsNotAnExpression {
type_name: variable_usage.name.clone(),
});
}
NamedEntity::Variable(variable) => {
substitution = compose_substitutions(ctx, &substitution, &unify(ctx, &variable_usage.type_, &variable)?)?;
@@ -1277,7 +1263,7 @@ impl TypeChecker {
let (source, subst) = self.with_expression(ctx, &substitution, &struct_getter.source)?;
substitution = compose_substitutions(ctx, &substitution, &subst)?;
let field_type = match get_attr(ctx, &source.type_, &struct_getter.attribute)? {
let field_type = match get_attr(ctx, &NamedEntity::Variable(source.type_.clone()), &struct_getter.attribute)? {
StructAttr::Field(type_) => type_,
StructAttr::Method(type_) => type_,
};
@@ -1295,14 +1281,24 @@ impl TypeChecker {
))
}
fn with_block_expression(self: &Self, ctx: &Context, substitution: &SubstitutionMap, block: &ast::Block) -> Result<(ast::Block, SubstitutionMap)> {
fn with_block_expression(
self: &Self,
ctx: &Context,
substitution: &SubstitutionMap,
block: &ast::Block,
) -> Result<(ast::Block, SubstitutionMap)> {
let mut substitution = substitution.clone();
let (result, subst) = self.with_block(ctx, &substitution, &block)?;
substitution = compose_substitutions(ctx, &substitution, &subst)?;
Ok((result, substitution))
}
fn with_op(self: &Self, ctx: &Context, substitution: &SubstitutionMap, op: &ast::Operation) -> Result<(ast::Operation, SubstitutionMap)> {
fn with_op(
self: &Self,
ctx: &Context,
substitution: &SubstitutionMap,
op: &ast::Operation,
) -> Result<(ast::Operation, SubstitutionMap)> {
let mut substitution = substitution.clone();
let (expr_left, subst_left) = self.with_expression(ctx, &substitution, &op.left)?;
let (expr_right, subst_right) = self.with_expression(ctx, &substitution, &op.right)?;