do: Elaborate on pattern syntax, add binary as operator
This commit is contained in:
168
src/parser.rs
168
src/parser.rs
@@ -15,6 +15,7 @@ pub enum ParseError {
|
||||
Expected(TKind, Span),
|
||||
NotLiteral(TKind, Span),
|
||||
NotPattern(TKind, Span),
|
||||
NotType(TKind, Span),
|
||||
NotPrefix(TKind, Span),
|
||||
NotInfix(TKind, Span),
|
||||
NotPostfix(TKind, Span),
|
||||
@@ -27,6 +28,7 @@ impl Display for ParseError {
|
||||
Self::Expected(tk, loc) => write!(f, "{loc}: Expected {tk:?}."),
|
||||
Self::NotLiteral(tk, loc) => write!(f, "{loc}: {tk:?} is not valid in a literal."),
|
||||
Self::NotPattern(tk, loc) => write!(f, "{loc}: {tk:?} is not valid in a pattern."),
|
||||
Self::NotType(tk, loc) => write!(f, "{loc}: {tk:?} is not valid in a type."),
|
||||
Self::NotPrefix(tk, loc) => write!(f, "{loc}: {tk:?} is not a prefix operator."),
|
||||
Self::NotInfix(tk, loc) => write!(f, "{loc}: {tk:?} is not a infix operator."),
|
||||
Self::NotPostfix(tk, loc) => write!(f, "{loc}: {tk:?} is not a postfix operator."),
|
||||
@@ -155,7 +157,7 @@ impl<'t> Parser<'t> {
|
||||
})
|
||||
}
|
||||
|
||||
/// Parses an expression into a vec unless the next token is `end`
|
||||
/// Parses a P unless the next token is `end`
|
||||
pub fn opt<P: Parse<'t>>(&mut self, level: P::Prec, end: TKind) -> PResult<Option<P>> {
|
||||
let out = match self.peek_if(end) {
|
||||
None => Some(self.parse(level)?),
|
||||
@@ -179,8 +181,8 @@ pub trait Parse<'t> {
|
||||
}
|
||||
|
||||
impl<'t> Parse<'t> for Literal {
|
||||
type Prec = usize;
|
||||
fn parse(p: &mut Parser<'t>, _level: usize) -> PResult<Self> {
|
||||
type Prec = ();
|
||||
fn parse(p: &mut Parser<'t>, _level: ()) -> PResult<Self> {
|
||||
let tok = p.peek()?;
|
||||
Ok(match tok.kind {
|
||||
TKind::True => p.consume().then(Literal::Bool(true)),
|
||||
@@ -209,9 +211,10 @@ impl<'t> Parse<'t> for Literal {
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum PPrec {
|
||||
Min,
|
||||
Typed,
|
||||
Tuple,
|
||||
Alt,
|
||||
NoTopAlt,
|
||||
Max,
|
||||
}
|
||||
|
||||
impl<'t> Parse<'t> for Pat {
|
||||
@@ -223,7 +226,7 @@ impl<'t> Parse<'t> for Pat {
|
||||
// Prefix
|
||||
let mut head = match tok.kind {
|
||||
TKind::True | TKind::False | TKind::Character | TKind::Integer | TKind::String => {
|
||||
Pat::Lit(p.parse(0)?)
|
||||
Pat::Lit(p.parse(())?)
|
||||
}
|
||||
TKind::Identifier => match tok.lexeme.as_str() {
|
||||
"_" => p.consume().then(Pat::Ignore),
|
||||
@@ -254,12 +257,15 @@ impl<'t> Parse<'t> for Pat {
|
||||
let kind = tok.kind;
|
||||
|
||||
head = match kind {
|
||||
TKind::Bar if level < PPrec::Alt => {
|
||||
Pat::Alt(p.consume().list_bare(vec![head], PPrec::Alt, kind)?)
|
||||
TKind::Colon if level > PPrec::Typed => {
|
||||
Pat::Typed(head.into(), p.consume().parse(())?)
|
||||
}
|
||||
TKind::Comma if level < PPrec::Tuple => {
|
||||
TKind::Comma if level > PPrec::Tuple => {
|
||||
Pat::Tuple(p.consume().list_bare(vec![head], PPrec::Tuple, kind)?)
|
||||
}
|
||||
TKind::Bar if level > PPrec::Alt => {
|
||||
Pat::Alt(p.consume().list_bare(vec![head], PPrec::Alt, kind)?)
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
}
|
||||
@@ -268,13 +274,44 @@ impl<'t> Parse<'t> for Pat {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'t> Parse<'t> for Ty {
|
||||
type Prec = ();
|
||||
|
||||
fn parse(p: &mut Parser<'t>, level: Self::Prec) -> PResult<Self>
|
||||
where Self: Sized {
|
||||
let tok = p.peek()?;
|
||||
|
||||
let head = match tok.kind {
|
||||
TKind::Identifier => match tok.lexeme.as_str() {
|
||||
"_" => p.consume().then(Ty::Infer),
|
||||
_ => Ty::Named(p.take_lexeme().expect("should have Token")),
|
||||
},
|
||||
TKind::LBrack => {
|
||||
let ty = p.consume().parse(level)?;
|
||||
match p.next()? {
|
||||
Token { kind: TKind::Semi, .. } => {
|
||||
let ty = Ty::Array(ty, p.parse(Prec::Binary.next())?);
|
||||
p.next_if(TKind::RBrack)?;
|
||||
ty
|
||||
}
|
||||
Token { kind: TKind::RBrack, .. } => Ty::Slice(ty),
|
||||
tok => Err(ParseError::NotType(tok.kind, tok.span))?,
|
||||
}
|
||||
}
|
||||
_ => Err(ParseError::NotType(tok.kind, tok.span))?,
|
||||
};
|
||||
|
||||
Ok(head)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'t> Parse<'t> for MatchArm {
|
||||
type Prec = usize;
|
||||
fn parse(p: &mut Parser<'t>, _level: usize) -> PResult<Self> {
|
||||
fn parse(p: &mut Parser<'t>, level: usize) -> PResult<Self> {
|
||||
p.next_if(TKind::Bar).ok();
|
||||
Ok(MatchArm(
|
||||
p.list(vec![], PPrec::Min, TKind::Bar, TKind::FatArrow)?,
|
||||
p.parse(0)?,
|
||||
p.list(vec![], PPrec::Max, TKind::Bar, TKind::FatArrow)?,
|
||||
p.parse(level)?,
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -301,11 +338,11 @@ enum Prec {
|
||||
Assign,
|
||||
/// Constructor for a tuple
|
||||
Tuple,
|
||||
/// Constructor for a struct
|
||||
Make,
|
||||
/// The body of a function, conditional, etc.
|
||||
Body,
|
||||
/// The short-circuiting logical operators [Prec::LogOr], [Prec::LogAnd]
|
||||
/// Constructor for a struct
|
||||
Make,
|
||||
/// The conditional of an `if` or `while` (which is really an `if`)
|
||||
Logical,
|
||||
/// The short-circuiting "boolean or" operator
|
||||
LogOr,
|
||||
@@ -372,17 +409,18 @@ pub enum Ps {
|
||||
fn from_prefix(token: &Token) -> PResult<(Ps, Prec)> {
|
||||
Ok(match token.kind {
|
||||
TKind::Do => (Ps::Op(Op::Do), Prec::Do),
|
||||
|
||||
TKind::Identifier => (Ps::Id, Prec::Max),
|
||||
TKind::Grave => (Ps::Mid, Prec::Max),
|
||||
TKind::ColonColon => (Ps::Op(Op::Path), Prec::Max),
|
||||
TKind::True | TKind::False | TKind::Character | TKind::Integer | TKind::String => {
|
||||
(Ps::Lit, Prec::Max)
|
||||
}
|
||||
|
||||
TKind::Identifier => (Ps::Id, Prec::Max),
|
||||
TKind::Grave => (Ps::Mid, Prec::Max),
|
||||
TKind::Fn => (Ps::Fn, Prec::Body),
|
||||
|
||||
TKind::Match => (Ps::Match, Prec::Body),
|
||||
TKind::Macro => (Ps::Op(Op::Macro), Prec::Assign),
|
||||
TKind::Let => (Ps::Let, Prec::Body),
|
||||
TKind::Let => (Ps::Let, Prec::Tuple),
|
||||
TKind::Const => (Ps::Const, Prec::Body),
|
||||
TKind::Loop => (Ps::Op(Op::Loop), Prec::Body),
|
||||
TKind::If => (Ps::Op(Op::If), Prec::Body),
|
||||
@@ -414,6 +452,17 @@ fn from_prefix(token: &Token) -> PResult<(Ps, Prec)> {
|
||||
|
||||
fn from_infix(token: &Token) -> PResult<(Ps, Prec)> {
|
||||
Ok(match token.kind {
|
||||
TKind::Semi => (Ps::Op(Op::Do), Prec::Do), // the inspiration
|
||||
TKind::As => (Ps::Op(Op::As), Prec::Body),
|
||||
TKind::Comma => (Ps::Op(Op::Tuple), Prec::Tuple),
|
||||
TKind::Dot => (Ps::Op(Op::Dot), Prec::Project),
|
||||
TKind::ColonColon => (Ps::Op(Op::Path), Prec::Max),
|
||||
TKind::AmpAmp => (Ps::Op(Op::LogAnd), Prec::LogAnd),
|
||||
TKind::BarBar => (Ps::Op(Op::LogOr), Prec::LogOr),
|
||||
TKind::Question => (Ps::Op(Op::Try), Prec::Unary),
|
||||
TKind::LParen => (Ps::Op(Op::Call), Prec::Extend),
|
||||
TKind::LBrack => (Ps::Op(Op::Index), Prec::Extend),
|
||||
// TKind::LCurly => (Ps::Make, Prec::Make),
|
||||
TKind::RParen | TKind::RBrack | TKind::RCurly => (Ps::End, Prec::Max),
|
||||
TKind::Eq => (Ps::Op(Op::Set), Prec::Assign),
|
||||
TKind::XorXor => (Ps::Op(Op::LogXor), Prec::Logical),
|
||||
@@ -435,45 +484,19 @@ fn from_infix(token: &Token) -> PResult<(Ps, Prec)> {
|
||||
TKind::Star => (Ps::Op(Op::Mul), Prec::Term),
|
||||
TKind::Slash => (Ps::Op(Op::Div), Prec::Term),
|
||||
TKind::Rem => (Ps::Op(Op::Rem), Prec::Term),
|
||||
TKind::ColonColon => (Ps::Op(Op::Path), Prec::Max),
|
||||
TKind::Question => (Ps::End, Prec::Extend),
|
||||
kind => Err(ParseError::NotInfix(kind, token.span))?,
|
||||
})
|
||||
}
|
||||
|
||||
#[allow(clippy::match_single_binding, unused)]
|
||||
fn from_postfix(token: &Token) -> PResult<(Ps, Prec)> {
|
||||
Ok(match token.kind {
|
||||
TKind::Semi => (Ps::Op(Op::Do), Prec::Do), // the inspiration
|
||||
TKind::Comma => (Ps::Op(Op::Tuple), Prec::Tuple),
|
||||
TKind::Dot => (Ps::Op(Op::Dot), Prec::Project),
|
||||
TKind::ColonColon => (Ps::Op(Op::Path), Prec::Max),
|
||||
TKind::AmpAmp => (Ps::Op(Op::LogAnd), Prec::LogAnd),
|
||||
TKind::BarBar => (Ps::Op(Op::LogOr), Prec::LogOr),
|
||||
TKind::Question => (Ps::Op(Op::Try), Prec::Unary),
|
||||
TKind::LParen => (Ps::Op(Op::Call), Prec::Extend),
|
||||
TKind::LBrack => (Ps::Op(Op::Index), Prec::Extend),
|
||||
TKind::LCurly => (Ps::Make, Prec::Make),
|
||||
kind => Err(ParseError::NotPostfix(kind, token.span))?,
|
||||
// _ => (Ps::End, Prec::Max),
|
||||
})
|
||||
}
|
||||
|
||||
#[rustfmt::skip]
|
||||
fn should_coagulate(prev: Op, op: Op) -> bool {
|
||||
prev == op && match prev {
|
||||
Op::LogAnd => true,
|
||||
Op::LogOr => true,
|
||||
Op::Dot => false,
|
||||
Op::Path => true,
|
||||
Op::Lt => false,
|
||||
Op::Leq => false,
|
||||
Op::Eq => false,
|
||||
Op::Neq => false,
|
||||
Op::Geq => false,
|
||||
Op::Gt => false,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
impl<'t> Parse<'t> for Expr {
|
||||
type Prec = usize;
|
||||
|
||||
@@ -496,12 +519,12 @@ impl<'t> Parse<'t> for Expr {
|
||||
|
||||
Ps::Id => Expr::Id(p.take_lexeme().expect("should have ident")),
|
||||
Ps::Mid => Expr::MetId(p.consume().next_if(TKind::Identifier)?.lexeme),
|
||||
Ps::Lit => Expr::Lit(p.parse(MIN)?),
|
||||
Ps::Lit => Expr::Lit(p.parse(())?),
|
||||
Ps::Let => Expr::Let(
|
||||
p.consume().parse(PPrec::NoTopAlt)?,
|
||||
p.opt_if(prec.next(), TKind::Eq)?,
|
||||
p.consume().parse(PPrec::Alt)?,
|
||||
p.opt_if(prec.value(), TKind::Eq)?,
|
||||
),
|
||||
Ps::Const => Expr::Const(p.consume().parse(PPrec::NoTopAlt)?, {
|
||||
Ps::Const => Expr::Const(p.consume().parse(PPrec::Tuple)?, {
|
||||
p.next_if(TKind::Eq)?;
|
||||
p.parse(prec.next())?
|
||||
}),
|
||||
@@ -514,7 +537,7 @@ impl<'t> Parse<'t> for Expr {
|
||||
),
|
||||
Ps::Match => Expr::Match(p.consume().parse(Prec::Logical.value())?, {
|
||||
p.next_if(TKind::LCurly)?;
|
||||
p.list(vec![], 0, TKind::Comma, TKind::RCurly)?
|
||||
p.list(vec![], prec.next(), TKind::Comma, TKind::RCurly)?
|
||||
}),
|
||||
Ps::Op(Op::Block) => Expr::Op(
|
||||
Op::Block,
|
||||
@@ -576,10 +599,30 @@ impl<'t> Parse<'t> for Expr {
|
||||
&& let Ok((op, prec)) = from_postfix(tok)
|
||||
&& level <= prec.prev()
|
||||
&& op != Ps::End
|
||||
{
|
||||
// let kind = tok.kind;
|
||||
let span = span.merge(p.span());
|
||||
// p.consume();
|
||||
head = match (op, &head) {
|
||||
(Ps::Make, Expr::Op(Op::Path, _) | Expr::Id(_) | Expr::MetId(_)) => Expr::Make(
|
||||
head.anno(span).into(),
|
||||
p.consume().list(vec![], (), TKind::Comma, TKind::RCurly)?,
|
||||
),
|
||||
_ => break,
|
||||
};
|
||||
}
|
||||
|
||||
// Infix
|
||||
while let Ok(tok) = p.peek()
|
||||
&& let Ok((op, prec)) = from_infix(tok)
|
||||
&& level <= prec.prev()
|
||||
&& op != Ps::End
|
||||
{
|
||||
let kind = tok.kind;
|
||||
let span = span.merge(p.span());
|
||||
|
||||
p.consume();
|
||||
|
||||
head = match op {
|
||||
Ps::Make => Expr::Make(
|
||||
head.anno(span).into(),
|
||||
@@ -596,27 +639,8 @@ impl<'t> Parse<'t> for Expr {
|
||||
Ps::Op(op @ (Op::Do | Op::Tuple | Op::Dot | Op::Path | Op::LogAnd | Op::LogOr)) => {
|
||||
Expr::Op(op, p.list_bare(vec![head.anno(span)], prec.next(), kind)?)
|
||||
}
|
||||
Ps::Op(op) => Expr::Op(op, vec![head.anno(span)]),
|
||||
_ => unimplemented!("postfix {op:?}"),
|
||||
};
|
||||
}
|
||||
|
||||
// Infix
|
||||
while let Ok(tok) = p.peek()
|
||||
&& let Ok((op, prec)) = from_infix(tok)
|
||||
&& level <= prec.prev()
|
||||
&& op != Ps::End
|
||||
{
|
||||
let span = span.merge(p.span());
|
||||
p.consume();
|
||||
|
||||
head = match (op, head) {
|
||||
// controls expression chaining vs coagulating
|
||||
(Ps::Op(op), Expr::Op(prev, mut args)) if should_coagulate(prev, op) => {
|
||||
args.push(p.parse(prec.next())?);
|
||||
Expr::Op(op, args)
|
||||
}
|
||||
(Ps::Op(op), head) => Expr::Op(op, vec![head.anno(span), p.parse(prec.next())?]),
|
||||
Ps::Op(op @ Op::Try) => Expr::Op(op, vec![head.anno(span)]),
|
||||
Ps::Op(op) => Expr::Op(op, vec![head.anno(span), p.parse(prec.next())?]),
|
||||
_ => unimplemented!("infix {op:?}"),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user