182 lines
6.8 KiB
Rust
182 lines
6.8 KiB
Rust
use super::{PResult, PResultExt, Parse, ParseError, Parser};
|
|
use crate::{
|
|
ast::*,
|
|
token::{TKind, Token},
|
|
};
|
|
|
|
/// Precedence levels of value and type pattern expressions.
|
|
///
|
|
/// Lower (toward [Prec::Min]) precedence levels can contain
|
|
/// all higher (toward [Prec::Max]) precedence levels.
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
|
pub enum Prec {
|
|
/// The lowest precedence
|
|
Min,
|
|
/// "Alternate" pattern: `Pat | Pat`
|
|
Alt,
|
|
/// Tuple pattern: `Pat,+`
|
|
Tuple,
|
|
/// Type annotation: `Pat : Pat`
|
|
Typed,
|
|
/// Function pattern: `Pat -> Pat`
|
|
Fn,
|
|
/// Range pattern: `Pat .. Pat`, `Pat ..= Pat`
|
|
Range,
|
|
/// The highest precedence
|
|
Max,
|
|
}
|
|
|
|
macro_rules! intify {
|
|
($enum:ident($value:path) = $min:ident, $max: ident, $($variant:ident),*$(,)?) => {
|
|
#[expect(non_upper_case_globals)] {
|
|
const $min: u32 = $enum::$min as _;
|
|
const $max: u32 = $enum::$max as _;
|
|
$(const $variant: u32 = $enum::$variant as _;)*
|
|
match $value {
|
|
..=$min => $enum::$min,
|
|
$($variant => $enum::$variant,)*
|
|
$max.. => $enum::$max,
|
|
}
|
|
}};
|
|
}
|
|
|
|
impl Prec {
|
|
const fn from_int(value: u32) -> Self {
|
|
intify! {Prec(value) = Min, Max, Alt, Tuple, Typed, Fn, Range}
|
|
}
|
|
|
|
/// Returns the level of precedence higher than this one
|
|
const fn next(self) -> Self {
|
|
Self::from_int(self as u32 + 1)
|
|
}
|
|
}
|
|
|
|
/// Tries to map the incoming Token to a [pattern operator](PatOp)
|
|
/// and its [precedence level](Prec)
|
|
fn from_infix(token: &Token) -> Option<(PatOp, Prec)> {
|
|
Some(match token.kind {
|
|
TKind::Arrow => (PatOp::Fn, Prec::Fn),
|
|
TKind::Bar => (PatOp::Alt, Prec::Alt),
|
|
TKind::Colon => (PatOp::Typed, Prec::Typed),
|
|
TKind::Comma => (PatOp::Tuple, Prec::Tuple),
|
|
TKind::DotDot => (PatOp::RangeEx, Prec::Range),
|
|
TKind::DotDotEq => (PatOp::RangeIn, Prec::Range),
|
|
_ => None?,
|
|
})
|
|
}
|
|
|
|
impl<'t> Parse<'t> for Pat {
|
|
type Prec = Prec;
|
|
|
|
fn parse(p: &mut Parser<'t>, level: Prec) -> PResult<Self> {
|
|
let tok = p.peek()?;
|
|
|
|
// Prefix
|
|
let mut head = match tok.kind {
|
|
TKind::Fn => return p.consume().parse(Prec::Fn),
|
|
TKind::True | TKind::False | TKind::Character | TKind::Integer | TKind::String => {
|
|
Pat::Lit(p.parse(())?)
|
|
}
|
|
TKind::Bar => p.consume().parse(level)?,
|
|
TKind::Bang => p.consume().then(Pat::Never),
|
|
TKind::Amp => Pat::Op(PatOp::Ref, vec![p.consume().parse(Prec::Max)?]),
|
|
TKind::Star => Pat::Op(PatOp::Ptr, vec![p.consume().parse(Prec::Max)?]),
|
|
TKind::Mut => Pat::Op(PatOp::Mut, vec![p.consume().parse(Prec::Max)?]),
|
|
TKind::Pub => Pat::Op(PatOp::Pub, vec![p.consume().parse(Prec::Max)?]),
|
|
TKind::AmpAmp => Pat::Op(
|
|
PatOp::Ref,
|
|
vec![Pat::Op(PatOp::Ref, vec![p.consume().parse(Prec::Max)?])],
|
|
),
|
|
TKind::Identifier => match tok.lexeme.str() {
|
|
Some("_") => p.consume().then(Pat::Ignore),
|
|
_ => {
|
|
let mut path: Path = p.parse(())?;
|
|
// TODO: make these postfix.
|
|
match p.peek().map(Token::kind) {
|
|
Ok(TKind::LParen) => Pat::NamedTuple(path, p.parse(Prec::Typed)?),
|
|
Ok(TKind::LCurly) if level <= Prec::Tuple.next() => Pat::NamedStruct(
|
|
path,
|
|
p.consume()
|
|
.opt(Prec::Tuple, TKind::RCurly)?
|
|
.unwrap_or_else(|| Box::new(Pat::Op(PatOp::Tuple, vec![]))),
|
|
),
|
|
Ok(_) | Err(ParseError::EOF(_)) => match path.parts.len() {
|
|
1 => Self::Name(path.parts.pop().expect("name has 1 part")),
|
|
_ => Self::Path(path),
|
|
},
|
|
Err(e) => Err(e)?,
|
|
}
|
|
}
|
|
},
|
|
TKind::Grave => Pat::MetId(p.consume().next()?.lexeme.to_string()),
|
|
TKind::DotDot => Pat::Op(
|
|
PatOp::Rest,
|
|
// Identifier in Rest position always becomes binder
|
|
match p.consume().peek().allow_eof()?.map(Token::kind) {
|
|
Some(TKind::Identifier) => vec![Pat::Name(
|
|
p.take_lexeme()
|
|
.expect("should have lexeme")
|
|
.string()
|
|
.expect("should be string"),
|
|
)],
|
|
Some(TKind::Grave | TKind::Integer | TKind::Character) => vec![p.parse(level)?],
|
|
_ => vec![],
|
|
},
|
|
),
|
|
TKind::DotDotEq => Pat::Op(
|
|
PatOp::RangeIn,
|
|
match p.consume().peek().allow_eof()?.map(Token::kind) {
|
|
Some(TKind::Grave | TKind::Integer | TKind::Character) => vec![p.parse(level)?],
|
|
_ => vec![],
|
|
},
|
|
),
|
|
TKind::LParen => Pat::Op(
|
|
PatOp::Tuple,
|
|
p.consume()
|
|
.list(vec![], Prec::Typed, TKind::Comma, TKind::RParen)?,
|
|
),
|
|
TKind::LBrack => parse_array_pat(p)?,
|
|
_ => Err(ParseError::NotPattern(tok.kind, tok.span))?,
|
|
};
|
|
|
|
while let Ok(Some(tok)) = p.peek().allow_eof()
|
|
&& let Some((op, prec)) = from_infix(tok)
|
|
&& level <= prec
|
|
{
|
|
let kind = tok.kind;
|
|
head = match op {
|
|
PatOp::Typed => Pat::Op(PatOp::Typed, vec![head, p.consume().parse(prec.next())?]),
|
|
PatOp::Fn => Pat::Op(PatOp::Fn, vec![head, p.consume().parse(Prec::Fn.next())?]),
|
|
op @ (PatOp::RangeEx | PatOp::RangeIn) => Pat::Op(
|
|
op,
|
|
match p.consume().peek().map(Token::kind) {
|
|
Ok(TKind::Integer | TKind::Character | TKind::Identifier) => {
|
|
vec![head, p.parse(prec.next())?]
|
|
}
|
|
_ => vec![head],
|
|
},
|
|
),
|
|
op => Pat::Op(op, p.consume().list_bare(vec![head], prec.next(), kind)?),
|
|
}
|
|
}
|
|
Ok(head)
|
|
}
|
|
}
|
|
|
|
fn parse_array_pat(p: &mut Parser<'_>) -> PResult<Pat> {
|
|
if p.consume().peek()?.kind == TKind::RBrack {
|
|
p.consume();
|
|
return Ok(Pat::Op(PatOp::Slice, vec![]));
|
|
}
|
|
|
|
let item = p.parse(Prec::Tuple)?;
|
|
let repeat = p.opt_if(Prec::Tuple, TKind::Semi)?;
|
|
p.expect(TKind::RBrack)?;
|
|
|
|
Ok(match (repeat, item) {
|
|
(Some(repeat), item) => Pat::Op(PatOp::ArRep, vec![item, repeat]),
|
|
(None, Pat::Op(PatOp::Tuple, items)) => Pat::Op(PatOp::Slice, items),
|
|
(None, item) => Pat::Op(PatOp::Slice, vec![item]),
|
|
})
|
|
}
|