use super::{PResult, Parser};
use crate::ast::{Type, TypeKind};
use crate::source::token::{Punct, Tok};
use crate::words::{self, Level};
impl Parser<'_, '_, '_> {
pub(crate) fn ty(&mut self) -> PResult<Type> {
let ty = self.ty_plain()?;
if !self.eat_word(&words::r#in::WORD) {
return Ok(ty);
}
let range = self.binary(Level::Range)?;
let span = ty.span.to(range.span);
Ok(Type {
kind: TypeKind::Bounded {
base: Box::new(ty),
range: Box::new(range),
},
span,
})
}
pub(crate) fn ty_plain(&mut self) -> PResult<Type> {
let start = self.span();
let mut ty = if self.at(Punct::LParen) {
self.ty_paren()?
} else {
self.ty_path()?
};
while self.at(Punct::Question) {
self.bump();
ty = Type {
kind: TypeKind::Optional(Box::new(ty)),
span: start.to(self.prev()),
};
}
Ok(ty)
}
fn ty_path(&mut self) -> PResult<Type> {
let start = self.span();
let path = self.path("a type")?;
let args = if self.at(Punct::Lt) {
self.type_args()?
} else {
Vec::new()
};
Ok(Type {
kind: TypeKind::Path { path, args },
span: start.to(self.prev()),
})
}
pub(crate) fn type_args(&mut self) -> PResult<Vec<Type>> {
self.need(Punct::Lt)?;
self.list(Punct::Gt, Self::ty_plain)
}
fn ty_paren(&mut self) -> PResult<Type> {
let start = self.need(Punct::LParen)?;
let items = self.list(Punct::RParen, Self::ty_plain)?;
if self.eat(Punct::Arrow) {
let ret = Box::new(self.ty_plain()?);
return Ok(Type {
kind: TypeKind::Fn { params: items, ret },
span: start.to(self.prev()),
});
}
match <[Type; 1]>::try_from(items) {
Ok([one]) => Ok(one),
Err(items) => Ok(Type {
kind: TypeKind::Tuple(items),
span: start.to(self.prev()),
}),
}
}
pub(crate) fn type_args_ahead(&self) -> Option<usize> {
let mut depth = 0usize;
let mut n = 0;
loop {
match self.nth(n) {
Tok::Punct(Punct::Lt) => depth += 1,
Tok::Punct(Punct::Gt) => {
depth -= 1;
if depth == 0 {
let after = self.nth(n + 1);
let call = matches!(
after,
Tok::Punct(Punct::LParen | Punct::LBrace | Punct::Dot)
) || matches!(after, Tok::Newline | Tok::Eof);
return call.then_some(n + 1);
}
}
Tok::Ident(_) | Tok::Punct(Punct::Dot | Punct::Comma | Punct::Question) => {}
_ => return None,
}
n += 1;
}
}
}