use crate::types::Type;
pub fn parse(s: &str) -> Result<Type, String> {
let (ty, s_left) = parse_aux(s).map_err(|e| format!("{}\n when parsing: {}", e, s))?;
if !s_left.is_empty() {
return Err(format!("Type parse() error: extra closeparen\n when parsing: {}",s))
}
Ok(ty)
}
fn parse_aux(mut s: &str) -> Result<(Type, &str), String> {
let mut res = vec![];
fn finish(mut res: Vec<Type>) -> Result<Type, String> {
if res.is_empty() {
return Err("Type parse() error: unexpected empty parens or empty string in type".into())
}
if res.len() == 1 {
return Ok(res.pop().unwrap())
}
let head = res.remove(0);
match head {
Type::Var(_) => Err("Type parse() error: type variable is applied to args".into()),
Type::Term(name, args) => {
if !args.is_empty() {
Err("Type parse() error: Term type applied to args like ((list int) int)".into())
} else {
Ok(Type::Term(name, res))
}
}
}
}
loop {
s = s.trim();
if s.is_empty() || s.starts_with(')') {
if !s.is_empty() {
s = &s[1..];
}
return finish(res).map(|res| (res, s))
}
if s.starts_with('(') {
let (ty, s_new) = parse_aux(&s[1..])?;
s = s_new;
res.push(ty);
continue
}
let (item, s_new) = s.split_at(s.find(|c| c == ' ' || c == ')').unwrap_or(s.len()));
s = s_new;
if let Some(rest) = item.strip_prefix('t') {
if let Ok(i) = rest.parse::<usize>() {
res.push(Type::Var(i));
continue
}
}
if item == Type::ARROW {
if res.is_empty() {
return Err("Type parse() error: no args to the left of an arrow".into())
}
let ty_left = finish(res).map_err(|s| format!("during arrow rearranging: {}",s))?;
let (ty_right, s_new) = parse_aux(&s[1..])?;
s = s_new;
return Ok((Type::Term(Type::ARROW.into(), vec![ty_left, ty_right]),s));
}
res.push(Type::Term(item.into(), vec![]))
}
}
#[test]
fn test_parse_types() {
assert_eq!("int".parse::<Type>().unwrap(),
Type::Term("int".into(), vec![]));
assert_eq!("((int))".parse::<Type>().unwrap(),
Type::Term("int".into(), vec![]));
assert_eq!("list int".parse::<Type>().unwrap(),
Type::Term("list".into(), vec![
Type::Term("int".into(), vec![])
]));
assert_eq!("(foo -> bar)".parse::<Type>().unwrap(),
Type::Term(Type::ARROW.into(), vec![
Type::Term("foo".into(), vec![]),
Type::Term("bar".into(), vec![]),
]));
assert_eq!("foo -> bar".parse::<Type>().unwrap(),
Type::Term(Type::ARROW.into(), vec![
Type::Term("foo".into(), vec![]),
Type::Term("bar".into(), vec![]),
]));
assert_eq!("(foo -> bar -> baz)".parse::<Type>().unwrap(),
Type::Term(Type::ARROW.into(), vec![
Type::Term("foo".into(), vec![]),
Type::Term(Type::ARROW.into(), vec![
Type::Term("bar".into(), vec![]),
Type::Term("baz".into(), vec![]),
]),
]));
assert_eq!("t2".parse::<Type>().unwrap(),
Type::Var(2));
assert_eq!("(t0 -> t1) -> (list t0) -> (list t1)".parse::<Type>().unwrap(),
Type::Term(Type::ARROW.into(), vec![
Type::Term(Type::ARROW.into(), vec![
Type::Var(0),
Type::Var(1),
]),
Type::Term(Type::ARROW.into(), vec![
Type::Term("list".into(), vec![
Type::Var(0)
]),
Type::Term("list".into(), vec![
Type::Var(1)
]),
]),
]));
}