use bhc_ast::{Expr, FieldPat, Lit, ModuleName, Pat};
use bhc_intern::{Ident, Symbol};
use bhc_lexer::TokenKind;
use bhc_span::Span;
use crate::{ParseError, ParseResult, Parser};
impl<'src> Parser<'src> {
pub fn is_pattern_start(&self) -> bool {
match self.current_kind() {
Some(kind) => matches!(
kind,
TokenKind::Ident(_)
| TokenKind::QualIdent(_, _)
| TokenKind::ConId(_)
| TokenKind::QualConId(_, _)
| TokenKind::IntLit(_)
| TokenKind::FloatLit(_)
| TokenKind::CharLit(_)
| TokenKind::StringLit(_)
| TokenKind::LParen
| TokenKind::LBracket
| TokenKind::Underscore
| TokenKind::Tilde
| TokenKind::Bang
),
None => false,
}
}
pub fn parse_pattern(&mut self) -> ParseResult<Pat> {
self.enter_recursion()?;
let result = self.parse_infix_pattern();
self.exit_recursion();
result
}
fn parse_infix_pattern(&mut self) -> ParseResult<Pat> {
let mut pat = self.parse_app_pattern()?;
while let Some(tok) = self.current() {
match &tok.node.kind {
TokenKind::ConOperator(sym) => {
let op = Ident::new(*sym);
self.advance();
let rhs = self.parse_infix_pattern()?;
let span = pat.span().to(rhs.span());
pat = Pat::Infix(Box::new(pat), op, Box::new(rhs), span);
}
TokenKind::QualConOperator(qual, sym) => {
let qualified_name =
Symbol::intern(&format!("{}.{}", qual.as_str(), sym.as_str()));
let op = Ident::new(qualified_name);
self.advance();
let rhs = self.parse_infix_pattern()?;
let span = pat.span().to(rhs.span());
pat = Pat::Infix(Box::new(pat), op, Box::new(rhs), span);
}
_ => break,
}
}
Ok(pat)
}
fn parse_app_pattern(&mut self) -> ParseResult<Pat> {
let first = self.parse_atom_pattern()?;
match first {
Pat::Con(con, args, span) if args.is_empty() => {
let mut new_args = Vec::new();
while self.is_apat_start() {
new_args.push(self.parse_atom_pattern()?);
}
if new_args.is_empty() {
return Ok(Pat::Con(con, args, span));
}
let new_span = span.to(new_args.last().unwrap().span());
return Ok(Pat::Con(con, new_args, new_span));
}
Pat::Con(con, args, span) => {
return Ok(Pat::Con(con, args, span));
}
Pat::QualCon(module_name, con, args, span) if args.is_empty() => {
let mut new_args = Vec::new();
while self.is_apat_start() {
new_args.push(self.parse_atom_pattern()?);
}
if new_args.is_empty() {
return Ok(Pat::QualCon(module_name, con, args, span));
}
let new_span = span.to(new_args.last().unwrap().span());
return Ok(Pat::QualCon(module_name, con, new_args, new_span));
}
Pat::QualCon(module_name, con, args, span) => {
return Ok(Pat::QualCon(module_name, con, args, span));
}
_ => {}
}
Ok(first)
}
pub fn is_apat_start(&self) -> bool {
match self.current_kind() {
Some(kind) => matches!(
kind,
TokenKind::Ident(_)
| TokenKind::ConId(_) | TokenKind::QualConId(_, _) | TokenKind::IntLit(_)
| TokenKind::FloatLit(_)
| TokenKind::CharLit(_)
| TokenKind::StringLit(_)
| TokenKind::LParen
| TokenKind::LBracket
| TokenKind::Underscore
| TokenKind::Tilde | TokenKind::Bang ),
None => false,
}
}
pub fn parse_atom_pattern(&mut self) -> ParseResult<Pat> {
let tok = self.current().ok_or(ParseError::UnexpectedEof {
expected: "pattern".to_string(),
})?;
match &tok.node.kind.clone() {
TokenKind::Underscore => {
let span = tok.span;
self.advance();
Ok(Pat::Wildcard(span))
}
TokenKind::Ident(sym) => {
let ident = Ident::new(*sym);
let span = tok.span;
self.advance();
if self.eat(&TokenKind::At) {
let pat = self.parse_atom_pattern()?;
let new_span = span.to(pat.span());
Ok(Pat::As(ident, Box::new(pat), new_span))
} else {
Ok(Pat::Var(ident, span))
}
}
TokenKind::QualIdent(qual, name) => {
let full_name = format!("{}.{}", qual.as_str(), name.as_str());
let ident = Ident::from_str(&full_name);
let span = tok.span;
self.advance();
Ok(Pat::Var(ident, span))
}
TokenKind::ConId(sym) => {
let ident = Ident::new(*sym);
let span = tok.span;
self.advance();
if self.check(&TokenKind::LBrace) {
return self.parse_record_pattern(ident, span);
}
Ok(Pat::Con(ident, vec![], span))
}
TokenKind::QualConId(qual, name) => {
let module_name = ModuleName {
parts: vec![*qual],
span: tok.span,
};
let ident = Ident::new(*name);
let span = tok.span;
self.advance();
if self.check(&TokenKind::LBrace) {
return self.parse_qual_record_pattern(module_name, ident, span);
}
Ok(Pat::QualCon(module_name, ident, vec![], span))
}
TokenKind::IntLit(ref lit) => {
let span = tok.span;
let value = self.parse_int_literal(&lit.text, span)?;
self.advance();
Ok(Pat::Lit(Lit::Int(value), span))
}
TokenKind::FloatLit(ref lit) => {
let span = tok.span;
let value = self.parse_float_literal(&lit.text, span)?;
self.advance();
Ok(Pat::Lit(Lit::Float(value), span))
}
TokenKind::CharLit(c) => {
let span = tok.span;
let c = *c;
self.advance();
Ok(Pat::Lit(Lit::Char(c), span))
}
TokenKind::StringLit(s) => {
let span = tok.span;
let s = s.clone();
self.advance();
Ok(Pat::Lit(Lit::String(s), span))
}
TokenKind::LParen => self.parse_paren_pattern(),
TokenKind::LBracket => self.parse_list_pattern(),
TokenKind::Tilde => {
let start = tok.span;
self.advance();
let pat = self.parse_atom_pattern()?;
let span = start.to(pat.span());
Ok(Pat::Lazy(Box::new(pat), span))
}
TokenKind::Bang => {
let start = tok.span;
self.advance();
let pat = self.parse_atom_pattern()?;
let span = start.to(pat.span());
Ok(Pat::Bang(Box::new(pat), span))
}
_ => Err(ParseError::Unexpected {
found: tok.node.kind.description().to_string(),
expected: "pattern".to_string(),
span: tok.span,
}),
}
}
fn parse_paren_pattern(&mut self) -> ParseResult<Pat> {
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(Pat::Con(Ident::from_str("()"), vec![], span));
}
let first = self.parse_pattern()?;
if self.eat(&TokenKind::Arrow) {
let view_expr = self.pat_to_expr(&first)?;
let result_pat = self.parse_pattern()?;
let end = self.expect(&TokenKind::RParen)?;
let span = start.to(end.span);
return Ok(Pat::View(Box::new(view_expr), Box::new(result_pat), span));
}
if matches!(&first, Pat::Var(..)) && self.is_apat_start() {
let save_pos = self.pos;
let mut args: Vec<Pat> = Vec::new();
while self.is_apat_start() && !self.check(&TokenKind::Arrow) {
args.push(self.parse_atom_pattern()?);
}
if self.eat(&TokenKind::Arrow) {
let mut view_expr = self.pat_to_expr(&first)?;
for arg in &args {
let arg_expr = self.pat_to_expr(arg)?;
let new_span = view_expr.span().to(arg_expr.span());
view_expr = Expr::App(Box::new(view_expr), Box::new(arg_expr), new_span);
}
let result_pat = self.parse_pattern()?;
let end = self.expect(&TokenKind::RParen)?;
let span = start.to(end.span);
return Ok(Pat::View(Box::new(view_expr), Box::new(result_pat), span));
}
self.pos = save_pos;
}
if self.eat(&TokenKind::DoubleColon) {
let ty = self.parse_type()?;
let end = self.expect(&TokenKind::RParen)?;
let span = start.to(end.span);
return Ok(Pat::Ann(Box::new(first), ty, span));
}
if self.eat(&TokenKind::Comma) {
let mut pats = vec![first];
loop {
pats.push(self.parse_pattern()?);
if !self.eat(&TokenKind::Comma) {
break;
}
}
let end = self.expect(&TokenKind::RParen)?;
let span = start.to(end.span);
Ok(Pat::Tuple(pats, span))
} else {
let end = self.expect(&TokenKind::RParen)?;
let span = start.to(end.span);
Ok(Pat::Paren(Box::new(first), span))
}
}
fn pat_to_expr(&self, pat: &Pat) -> ParseResult<Expr> {
use bhc_ast::Expr;
match pat {
Pat::Var(name, span) => {
let name_str = name.name.as_str();
if let Some(dot_pos) = name_str.rfind('.') {
let qualifier = &name_str[..dot_pos];
let local = &name_str[dot_pos + 1..];
if !qualifier.is_empty() && !local.is_empty() {
let module_name = ModuleName {
parts: vec![Symbol::intern(qualifier)],
span: *span,
};
let local_ident = Ident::from_str(local);
return Ok(Expr::QualVar(module_name, local_ident, *span));
}
}
Ok(Expr::Var(*name, *span))
}
Pat::Con(name, args, span) => {
if args.is_empty() {
Ok(Expr::Con(*name, *span))
} else {
let mut result = Expr::Con(*name, *span);
for arg in args {
let arg_expr = self.pat_to_expr(arg)?;
let new_span = result.span().to(arg_expr.span());
result = Expr::App(Box::new(result), Box::new(arg_expr), new_span);
}
Ok(result)
}
}
Pat::QualCon(module_name, name, args, span) => {
if args.is_empty() {
Ok(Expr::QualCon(module_name.clone(), *name, *span))
} else {
let mut result = Expr::QualCon(module_name.clone(), *name, *span);
for arg in args {
let arg_expr = self.pat_to_expr(arg)?;
let new_span = result.span().to(arg_expr.span());
result = Expr::App(Box::new(result), Box::new(arg_expr), new_span);
}
Ok(result)
}
}
Pat::Lit(lit, span) => Ok(Expr::Lit(lit.clone(), *span)),
Pat::Paren(inner, span) => {
let inner_expr = self.pat_to_expr(inner)?;
Ok(Expr::Paren(Box::new(inner_expr), *span))
}
Pat::Tuple(elems, span) => {
let mut exprs = Vec::new();
for elem in elems {
exprs.push(self.pat_to_expr(elem)?);
}
Ok(Expr::Tuple(exprs, *span))
}
Pat::List(elems, span) => {
let mut exprs = Vec::new();
for elem in elems {
exprs.push(self.pat_to_expr(elem)?);
}
Ok(Expr::List(exprs, *span))
}
Pat::Wildcard(span) => {
Ok(Expr::Var(Ident::from_str("_"), *span))
}
_ => Err(ParseError::Unexpected {
found: "complex pattern".to_string(),
expected: "simple expression for view pattern".to_string(),
span: pat.span(),
}),
}
}
fn parse_list_pattern(&mut self) -> ParseResult<Pat> {
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(Pat::List(vec![], span));
}
let mut pats = vec![self.parse_pattern()?];
while self.eat(&TokenKind::Comma) {
if self.check(&TokenKind::RBracket) {
break;
}
pats.push(self.parse_pattern()?);
}
let end = self.expect(&TokenKind::RBracket)?;
let span = start.to(end.span);
Ok(Pat::List(pats, span))
}
fn parse_record_pattern(&mut self, con: Ident, start: Span) -> ParseResult<Pat> {
self.expect(&TokenKind::LBrace)?;
let mut fields = Vec::new();
let mut has_wildcard = false;
if !self.check(&TokenKind::RBrace) {
if self.eat(&TokenKind::DotDot) {
has_wildcard = true;
} else {
fields.push(self.parse_field_pat()?);
while self.eat(&TokenKind::Comma) {
if self.check(&TokenKind::RBrace) {
break;
}
if self.eat(&TokenKind::DotDot) {
has_wildcard = true;
break;
}
fields.push(self.parse_field_pat()?);
}
}
}
let end = self.expect(&TokenKind::RBrace)?;
let span = start.to(end.span);
Ok(Pat::Record(con, fields, has_wildcard, span))
}
fn parse_qual_record_pattern(
&mut self,
module_name: ModuleName,
con: Ident,
start: Span,
) -> ParseResult<Pat> {
self.expect(&TokenKind::LBrace)?;
let mut fields = Vec::new();
let mut has_wildcard = false;
if !self.check(&TokenKind::RBrace) {
if self.eat(&TokenKind::DotDot) {
has_wildcard = true;
} else {
fields.push(self.parse_field_pat()?);
while self.eat(&TokenKind::Comma) {
if self.check(&TokenKind::RBrace) {
break;
}
if self.eat(&TokenKind::DotDot) {
has_wildcard = true;
break;
}
fields.push(self.parse_field_pat()?);
}
}
}
let end = self.expect(&TokenKind::RBrace)?;
let span = start.to(end.span);
Ok(Pat::QualRecord(
module_name,
con,
fields,
has_wildcard,
span,
))
}
fn parse_field_pat(&mut self) -> ParseResult<FieldPat> {
let tok = self.current().ok_or(ParseError::UnexpectedEof {
expected: "field name".to_string(),
})?;
let (qualifier, name, span) = match &tok.node.kind {
TokenKind::Ident(sym) => (None, Ident::new(*sym), tok.span),
TokenKind::QualIdent(qual, sym) => {
let module_name = ModuleName {
parts: vec![*qual],
span: tok.span,
};
(Some(module_name), Ident::new(*sym), tok.span)
}
_ => {
return Err(ParseError::Unexpected {
found: tok.node.kind.description().to_string(),
expected: "field name".to_string(),
span: tok.span,
});
}
};
self.advance();
let pat = if self.eat(&TokenKind::Eq) {
Some(self.parse_pattern()?)
} else {
None };
let end_span = pat.as_ref().map(|p| p.span()).unwrap_or(span);
let full_span = span.to(end_span);
Ok(FieldPat {
qualifier,
name,
pat,
span: full_span,
})
}
}