Skip to content
This repository has been archived by the owner on Oct 10, 2024. It is now read-only.

Commit

Permalink
Refactored function type to a seperate type
Browse files Browse the repository at this point in the history
  • Loading branch information
melody-notpond committed Mar 26, 2024
1 parent b0c63c5 commit b3f8d27
Show file tree
Hide file tree
Showing 9 changed files with 108 additions and 56 deletions.
17 changes: 17 additions & 0 deletions src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ pub enum Type {
Typevar(usize),
Name(String),
Generic(String),
Func(Vec<Type>, Box<Type>),
App(Box<Type>, Vec<Type>),
}

Expand All @@ -24,6 +25,22 @@ impl Display for Type {
Type::Name(n) => write!(f, "{}", n),
Type::Generic(g) => write!(f, "'{}", g),

Type::Func(args, ret) => {
write!(f, "Fn[")?;

let mut first = true;
for arg in args {
if first {
write!(f, "{}", arg)?;
first = false;
} else {
write!(f, ", {}", arg)?;
}
}

write!(f, "] -> {}", ret)
}

Type::App(f_, a) => {
write!(f, "{}[", f_)?;
let mut first = true;
Expand Down
26 changes: 20 additions & 6 deletions src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,16 @@ impl Parser {
use OpAssoc::*;

let mut map = HashMap::new();
map.insert("::".to_owned(), Infix(0, Right));
map.insert("::".to_owned(), Infix(5, Right));
map.insert("+".to_owned(), Infix(10, Left));
map.insert("-".to_owned(), Infix(10, Left));
map.insert("*".to_owned(), Infix(20, Left));
map.insert("/".to_owned(), Infix(20, Left));
map.insert("**".to_owned(), Infix(30, Right));
map.insert("?".to_owned(), Postfix);
map.insert(".*".to_owned(), Postfix);
// map.insert("**".to_owned(), Infix(30, Right));
// map.insert("?".to_owned(), Postfix);
map.insert("!".to_owned(), Postfix);
map.insert(":=".to_owned(), Infix(0, Right));
map.insert("~".to_owned(), Prefix);
map.insert("!".to_owned(), Prefix);
map
}

Expand Down Expand Up @@ -618,7 +618,21 @@ impl Parser {
}
}
consume_token!(self.lexer, Token::RBrack, "type arguments must terminate in a `]`");
Ok(Type::App(Box::new(head), args))

match head {
Type::Name(n) if n == "Fn" => {
let ret = if let (Token::RArrow, ..) = self.lexer.peek() {
self.lexer.lex();
self.parse_type(generics)?
} else {
Type::Name("Unit".to_owned())
};

Ok(Type::Func(args, Box::new(ret)))
}

head => Ok(Type::App(Box::new(head), args))
}
} else {
Ok(head)
}
Expand Down
105 changes: 63 additions & 42 deletions src/typecheck.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,6 @@ pub fn typecheck(ast: &[TopLevel]) -> Result<(), TypeError> {
let mut globals = HashMap::new();
let mut defined_types = HashMap::new();
let mut type_vars = Vec::new();
defined_types.insert("Fn".to_owned(), TypeData {
va_params: false,
params: vec!["A".to_owned(), "R".to_owned()],
variants: Vec::new(),
});
defined_types.insert("Tuple".to_owned(), TypeData {
va_params: true,
params: Vec::new(),
Expand All @@ -48,6 +43,11 @@ pub fn typecheck(ast: &[TopLevel]) -> Result<(), TypeError> {
params: Vec::new(),
variants: Vec::new(),
});
defined_types.insert("Pointer".to_owned(), TypeData {
va_params: false,
params: vec!["T".to_string()],
variants: Vec::new(),
});

// get all globals (functions and type definitions)
for top in ast {
Expand Down Expand Up @@ -76,21 +76,12 @@ pub fn typecheck(ast: &[TopLevel]) -> Result<(), TypeError> {
});
};

if variant.fields.is_empty() {
v.insert(GlobalData {
type_: self_type.clone(),
variant_of: Some(name.clone()),
});
} else {
v.insert(GlobalData {
type_: Type::App(
Box::new(Type::Name("Fn".to_owned())),
vec![
Type::App(Box::new(Type::Name("Tuple".to_owned())), variant.fields.clone()),
self_type.clone()]),
variant_of: Some(name.clone()),
});
}
v.insert(GlobalData {
type_: Type::Func(
variant.fields.clone(),
Box::new(self_type.clone())),
variant_of: Some(name.clone()),
});
}

let data = TypeData {
Expand All @@ -111,9 +102,7 @@ pub fn typecheck(ast: &[TopLevel]) -> Result<(), TypeError> {

let fn_args: Vec<_> = args.iter().map(|(_, t)| t.clone()).collect();
v.insert(GlobalData {
type_: Type::App(
Box::new(Type::Name("Fn".to_owned())),
vec![Type::App(Box::new(Type::Name("Tuple".to_owned())), fn_args), ret.clone()]),
type_: Type::Func(fn_args, Box::new(ret.clone())),
variant_of: None,
});
}
Expand Down Expand Up @@ -498,6 +487,11 @@ fn instantiate_generics_with_map(
}
}

Type::Func(a, r) => Type::Func(
a.iter().map(|t| instantiate_generics_with_map(generics, type_vars, t)).collect(),
Box::new(instantiate_generics_with_map(generics, type_vars, r)),
),

Type::App(f, a) => Type::App(
Box::new(instantiate_generics_with_map(generics, type_vars, f)),
a.iter().map(|t| instantiate_generics_with_map(generics, type_vars, t)).collect()
Expand Down Expand Up @@ -534,14 +528,34 @@ fn verify_type(t: &Type, defined_types: &HashMap<String, TypeData>) -> Result<()
}
}

Type::Func(args, ret) => {
for arg in args {
let (Some(0) | None) = helper(arg, defined_types)?
else {
return Err(TypeError {
message: format!("function parameter {arg} should have 0 expected type parameters"),
});
};
}

let (Some(0) | None) = helper(ret, defined_types)?
else {
return Err(TypeError {
message: format!("function return {ret} should have 0 expected type parameters"),
});
};

Ok(Some(0))
}

Type::App(f, a) => {
for a in a {
if let Some(0) | None = helper(a, defined_types)? { }
let (Some(0) | None) = helper(a, defined_types)?
else {
return Err(TypeError {
message: format!("type parameter {a} should have 0 expected type parameters"),
});
}
};
}

let Some(expected_count) = helper(f, defined_types)?
Expand Down Expand Up @@ -575,28 +589,17 @@ fn valid_call<'a>(
args: &[Type],
) -> Result<&'a Type, TypeError> {
match f {
Type::App(fa, v) => {
let Type::Name(n) = &**fa
else {
Type::Func(params, ret) => {
if args.len() != params.len() {
return Err(TypeError {
message: format!("type {f} is not callable"),
});
};

if n != "Fn" {
return Err(TypeError {
message: format!("type {f} is not callable"),
message: format!("passed in {} arguments when expected {}", args.len(), params.len()),
});
}

if v.len() != 2 {
return Err(TypeError {
message: format!("type {f} is not a valid function type"),
})
for (a, t) in args.iter().zip(params.iter()) {
unify(type_vars, a, t)?;
}

unify(type_vars, &v[0], &Type::App(Box::new(Type::Name("Tuple".to_owned())), args.to_owned()))?;
Ok(&v[1])
Ok(&ret)
}

_ => Err(TypeError {
Expand Down Expand Up @@ -660,6 +663,24 @@ fn unify(
}

(Type::Name(n1), Type::Name(n2)) if n1 == n2 => Ok(t1.clone()),

(Type::Func(a1, r1), Type::Func(a2, r2)) => {
if a1.len() != a2.len() {
return Err(TypeError {
message: format!("types {} and {} cannot be unified", t1, t2),
});
}

let mut a = Vec::new();
for (a1, a2) in a1.iter().zip(a2.iter()) {
a.push(unify(type_vars, a1, a2)?);
}

let r = unify(type_vars, r1, r2)?;

Ok(Type::Func(a, Box::new(r)))
}

(Type::App(f1, a1), Type::App(f2, a2)) => {
let f = unify(type_vars, f1, f2)?;

Expand Down
2 changes: 1 addition & 1 deletion tests/amy/typecheck/generic_func.amy
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,6 @@ def main() do
let _ = id(2)
let _ = id(X)
let _ = id(Some(2))
let x = id(None)
let x = id(None())
x = Some(X)
end
4 changes: 2 additions & 2 deletions tests/amy/typecheck/generics.amy
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ type X as X end

def main() do
let x = Some(2)
x = None
let y = None
x = None()
let y = None()
y = Some(X)
end
2 changes: 1 addition & 1 deletion tests/amy/typecheck/generics.err.amy
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@ type X as X end

def main() do
let x = Some(2)
x = Some(X)
x = Some(X())
end
4 changes: 2 additions & 2 deletions tests/amy/typecheck/if.amy
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ type Bool as
end

def main() do
if True then
if True() then
else
end

if False then
if False() then
end
end
2 changes: 1 addition & 1 deletion tests/amy/typecheck/if.err.amy
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ type NotIfCompat as
end

def main() do
if A then
if A() then
end
end
2 changes: 1 addition & 1 deletion tests/amy/typecheck/no_ret.err.amy
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
type Bool as True False end

def rets_int() -> Int do
if False then
if False() then
return 0
end
end

0 comments on commit b3f8d27

Please sign in to comment.