use bhc_ast::{Constraint, ModuleName, TyVar, Type};
use bhc_intern::Ident;
use bhc_lexer::TokenKind;
use crate::{ParseError, ParseResult, Parser};
impl<'src> Parser<'src> {
pub fn parse_type(&mut self) -> ParseResult<Type> {
self.enter_recursion()?;
let result = self.parse_type_guarded();
self.exit_recursion();
result
}
fn parse_type_guarded(&mut self) -> ParseResult<Type> {
let start = self.current_span();
if self.check(&TokenKind::Forall) {
return self.parse_forall_type();
}
if let Some(constraints) = self.try_parse_context()? {
let ty = self.parse_fun_type()?;
let span = start.to(ty.span());
return Ok(Type::Constrained(constraints, Box::new(ty), span));
}
self.parse_fun_type()
}
pub(crate) fn try_parse_context(&mut self) -> ParseResult<Option<Vec<Constraint>>> {
let saved_pos = self.pos;
let constraints = if self.check(&TokenKind::LParen) {
self.advance();
if self.check(&TokenKind::RParen) {
self.advance();
if self.eat(&TokenKind::FatArrow) {
return Ok(Some(vec![]));
}
self.pos = saved_pos;
return Ok(None);
}
let mut constraints = vec![];
match self.try_parse_constraint() {
Ok(Some(c)) => constraints.push(c),
_ => {
self.pos = saved_pos;
return Ok(None);
}
}
while self.eat(&TokenKind::Comma) {
match self.try_parse_constraint() {
Ok(Some(c)) => constraints.push(c),
_ => {
self.pos = saved_pos;
return Ok(None);
}
}
}
if !self.eat(&TokenKind::RParen) {
self.pos = saved_pos;
return Ok(None);
}
constraints
} else {
match self.try_parse_constraint() {
Ok(Some(c)) => vec![c],
_ => {
self.pos = saved_pos;
return Ok(None);
}
}
};
if self.eat(&TokenKind::FatArrow) {
Ok(Some(constraints))
} else {
self.pos = saved_pos;
Ok(None)
}
}
fn try_parse_constraint(&mut self) -> ParseResult<Option<Constraint>> {
let start = self.current_span();
let class = match self.current_kind() {
Some(TokenKind::ConId(sym)) => {
let ident = Ident::new(*sym);
self.advance();
ident
}
_ => return Ok(None),
};
let mut args = vec![];
while self.is_atype_start() {
args.push(self.parse_atype()?);
}
let end_span = args.last().map(|t| t.span()).unwrap_or(start);
let span = start.to(end_span);
Ok(Some(Constraint { class, args, span }))
}
fn parse_fun_type(&mut self) -> ParseResult<Type> {
let lhs = self.parse_infix_type()?;
self.skip_doc_comments();
if self.eat(&TokenKind::Arrow) {
let rhs = self.parse_fun_type()?;
let span = lhs.span().to(rhs.span());
Ok(Type::Fun(Box::new(lhs), Box::new(rhs), span))
} else {
Ok(lhs)
}
}
fn parse_infix_type(&mut self) -> ParseResult<Type> {
let lhs = self.parse_app_type()?;
if let Some(TokenKind::ConOperator(sym)) = self.current_kind().cloned() {
let op = Ident::new(sym);
self.advance();
let rhs = self.parse_infix_type()?;
let span = lhs.span().to(rhs.span());
Ok(Type::InfixOp(Box::new(lhs), op, Box::new(rhs), span))
} else {
Ok(lhs)
}
}
fn parse_app_type(&mut self) -> ParseResult<Type> {
let mut ty = self.parse_atype()?;
while self.is_atype_start() {
let arg = self.parse_atype()?;
let span = ty.span().to(arg.span());
ty = Type::App(Box::new(ty), Box::new(arg), span);
}
Ok(ty)
}
pub fn is_atype_start(&self) -> bool {
match self.current_kind() {
Some(kind) => matches!(
kind,
TokenKind::Ident(_)
| TokenKind::ConId(_)
| TokenKind::QualConId(_, _)
| TokenKind::LParen
| TokenKind::LBracket
| TokenKind::IntLit(_)
| TokenKind::TickLBracket
| TokenKind::Bang
| TokenKind::Tilde
),
None => false,
}
}
pub fn parse_atype(&mut self) -> ParseResult<Type> {
let tok = self.current().ok_or(ParseError::UnexpectedEof {
expected: "type".to_string(),
})?;
match &tok.node.kind.clone() {
TokenKind::Ident(sym) => {
let ident = Ident::new(*sym);
let span = tok.span;
self.advance();
Ok(Type::Var(TyVar { name: ident, span }, span))
}
TokenKind::ConId(sym) => {
let ident = Ident::new(*sym);
let span = tok.span;
self.advance();
Ok(Type::Con(ident, span))
}
TokenKind::QualConId(qualifier, name) => {
let module_name = ModuleName {
parts: vec![*qualifier],
span: tok.span,
};
let ident = Ident::new(*name);
let span = tok.span;
self.advance();
Ok(Type::QualCon(module_name, ident, span))
}
TokenKind::LParen => self.parse_paren_type(),
TokenKind::LBracket => self.parse_list_type(),
TokenKind::IntLit(lit) => {
let span = tok.span;
let value = lit.parse().ok_or_else(|| ParseError::Unexpected {
found: "invalid integer".to_string(),
expected: "type-level natural".to_string(),
span,
})?;
if value < 0 {
return Err(ParseError::Unexpected {
found: "negative integer".to_string(),
expected: "type-level natural (non-negative)".to_string(),
span,
});
}
self.advance();
Ok(Type::NatLit(value as u64, span))
}
TokenKind::TickLBracket => self.parse_promoted_list(),
TokenKind::Bang => {
let start = tok.span;
self.advance();
let inner = self.parse_atype()?;
let span = start.to(inner.span());
Ok(Type::Bang(Box::new(inner), span))
}
TokenKind::Tilde => {
let start = tok.span;
self.advance();
let inner = self.parse_atype()?;
let span = start.to(inner.span());
Ok(Type::Lazy(Box::new(inner), span))
}
_ => Err(ParseError::Unexpected {
found: tok.node.kind.description().to_string(),
expected: "type".to_string(),
span: tok.span,
}),
}
}
fn parse_promoted_list(&mut self) -> ParseResult<Type> {
let start = self.current_span();
self.expect(&TokenKind::TickLBracket)?;
if self.eat(&TokenKind::RBracket) {
let span = start.to(self.tokens[self.pos - 1].span);
return Ok(Type::PromotedList(vec![], span));
}
let mut elems = vec![self.parse_type()?];
while self.eat(&TokenKind::Comma) {
elems.push(self.parse_type()?);
}
let end = self.expect(&TokenKind::RBracket)?;
let span = start.to(end.span);
Ok(Type::PromotedList(elems, span))
}
fn parse_paren_type(&mut self) -> ParseResult<Type> {
let start = self.current_span();
self.expect(&TokenKind::LParen)?;
if self.eat(&TokenKind::RParen) {
let span = start.to(self.tokens[self.pos - 1].span);
return Ok(Type::Tuple(vec![], span));
}
if self.eat(&TokenKind::Arrow) {
let end = self.expect(&TokenKind::RParen)?;
let span = start.to(end.span);
return Ok(Type::Con(Ident::from_str("->"), span));
}
if let Some(TokenKind::ConOperator(sym)) = self.current_kind().cloned() {
let saved = self.pos;
let ident = Ident::new(sym);
self.advance();
if self.check(&TokenKind::RParen) {
let end = self.expect(&TokenKind::RParen)?;
let span = start.to(end.span);
return Ok(Type::Con(ident, span));
}
self.pos = saved;
}
let first = self.parse_type()?;
if self.eat(&TokenKind::Comma) {
let mut types = vec![first];
loop {
types.push(self.parse_type()?);
if !self.eat(&TokenKind::Comma) {
break;
}
}
let end = self.expect(&TokenKind::RParen)?;
let span = start.to(end.span);
Ok(Type::Tuple(types, span))
} else {
let end = self.expect(&TokenKind::RParen)?;
let span = start.to(end.span);
Ok(Type::Paren(Box::new(first), span))
}
}
fn parse_list_type(&mut self) -> ParseResult<Type> {
let start = self.current_span();
self.expect(&TokenKind::LBracket)?;
if self.eat(&TokenKind::RBracket) {
let span = start.to(self.tokens[self.pos - 1].span);
return Ok(Type::Con(Ident::from_str("[]"), span));
}
let elem = self.parse_type()?;
let end = self.expect(&TokenKind::RBracket)?;
let span = start.to(end.span);
Ok(Type::List(Box::new(elem), span))
}
fn parse_forall_type(&mut self) -> ParseResult<Type> {
let start = self.current_span();
self.expect(&TokenKind::Forall)?;
let mut vars = Vec::new();
while let Some(tok) = self.current() {
match &tok.node.kind {
TokenKind::Ident(sym) => {
let name = Ident::new(*sym);
let span = tok.span;
self.advance();
vars.push(TyVar { name, span });
}
TokenKind::Dot => {
self.advance();
break;
}
_ => break,
}
}
let ty = self.parse_type()?;
let span = start.to(ty.span());
Ok(Type::Forall(vars, Box::new(ty), span))
}
}