use nom::character::complete::{multispace0, multispace1}; use nom::{alt, delimited, do_parse, map, named, opt, separated_list0, tag, terminated, tuple}; use super::ident; use super::util::comma; use crate::ast::{FunctionType, Type}; named!(pub function_type(&str) -> FunctionType, do_parse!( tag!("fn") >> multispace1 >> args: map!(opt!(terminated!(separated_list0!( comma, type_ ), multispace1)), |args| args.unwrap_or_default()) >> tag!("->") >> multispace1 >> ret: type_ >> (FunctionType { args, ret: Box::new(ret) }) )); named!(tuple_type(&str) -> Type, do_parse!( tag!("(") >> multispace0 >> fst: type_ >> comma >> rest: separated_list0!( comma, type_ ) >> multispace0 >> tag!(")") >> ({ let mut members = Vec::with_capacity(rest.len() + 1); members.push(fst); members.append(&mut rest.clone()); Type::Tuple(members) }) )); named!(pub type_(&str) -> Type, alt!( tag!("int") => { |_| Type::Int } | tag!("float") => { |_| Type::Float } | tag!("bool") => { |_| Type::Bool } | tag!("cstring") => { |_| Type::CString } | tag!("()") => { |_| Type::Unit } | tuple_type | function_type => { |ft| Type::Function(ft) }| ident => { |id| Type::Var(id) } | delimited!( tuple!(tag!("("), multispace0), type_, tuple!(tag!(")"), multispace0) ) )); #[cfg(test)] mod tests { use std::convert::TryFrom; use super::*; use crate::ast::Ident; #[test] fn simple_types() { assert_eq!(test_parse!(type_, "int"), Type::Int); assert_eq!(test_parse!(type_, "float"), Type::Float); assert_eq!(test_parse!(type_, "bool"), Type::Bool); assert_eq!(test_parse!(type_, "cstring"), Type::CString); assert_eq!(test_parse!(type_, "()"), Type::Unit); } #[test] fn no_arg_fn_type() { assert_eq!( test_parse!(type_, "fn -> int"), Type::Function(FunctionType { args: vec![], ret: Box::new(Type::Int) }) ); } #[test] fn fn_type_with_args() { assert_eq!( test_parse!(type_, "fn int, bool -> int"), Type::Function(FunctionType { args: vec![Type::Int, Type::Bool], ret: Box::new(Type::Int) }) ); } #[test] fn fn_taking_fn() { assert_eq!( test_parse!(type_, "fn fn int, bool -> bool, float -> float"), Type::Function(FunctionType { args: vec![ Type::Function(FunctionType { args: vec![Type::Int, Type::Bool], ret: Box::new(Type::Bool) }), Type::Float ], ret: Box::new(Type::Float) }) ) } #[test] fn parenthesized() { assert_eq!( test_parse!(type_, "fn (fn int, bool -> bool), float -> float"), Type::Function(FunctionType { args: vec![ Type::Function(FunctionType { args: vec![Type::Int, Type::Bool], ret: Box::new(Type::Bool) }), Type::Float ], ret: Box::new(Type::Float) }) ) } #[test] fn tuple() { assert_eq!( test_parse!(type_, "(int, int)"), Type::Tuple(vec![Type::Int, Type::Int]) ) } #[test] fn type_vars() { assert_eq!( test_parse!(type_, "fn x, y -> x"), Type::Function(FunctionType { args: vec![ Type::Var(Ident::try_from("x").unwrap()), Type::Var(Ident::try_from("y").unwrap()), ], ret: Box::new(Type::Var(Ident::try_from("x").unwrap())), }) ) } }