import crate::ParseError
import crate::operand::constructor_path
import crate::operand::range_or_literal
import crate::parser::Parser
import crate::token::token_text
import voltaic_ast::Ident
import voltaic_ast::Pattern
import voltaic_ast::RecordPatternField
import voltaic_lexer::Keyword
import voltaic_lexer::TokenKind
export(crate) pattern : &mut Parser -> Result<Pattern, ParseError>
pattern p =
span = .peek_span p
case &(.peek p).kind of
TokenKind::Ident text | text == "_" ->
.advance p
Ok $ Pattern::Wildcard span
TokenKind::Ident _ ->
name = (.ident "expected a pattern" p)?
if .check (&TokenKind::At) p
.advance p
if .check (&TokenKind::DotDot) p
.advance p
return Ok (Pattern::NamedRest { name, span = (span.0, (.prev_span p).1) })
inner = (pattern_app p)?
return Ok (Pattern::As { name, inner = Box::new inner, span = (span.0, (.prev_span p).1) })
if .check (&TokenKind::LBrace) p
return record_pattern p name.text span.0
Ok $ Pattern::Binding name
TokenKind::DotDot ->
.advance p
Ok $ Pattern::Rest span
TokenKind::LBracket ->
.advance p
items = (pattern_series p (&TokenKind::RBracket))?
(.expect (&TokenKind::RBracket) "expected `]` to close the slice pattern" p)?
Ok (Pattern::Slice { items, span = (span.0, (.prev_span p).1) })
TokenKind::DotDotDot ->
Err $ ParseError::r#with voltaic_diag::Code::EllipsisRestPattern "`...` left the language with doc 18 §7 — write a rest pattern Rust's way: `..` to discard, or `rest @ ..` to bind" span
TokenKind::LParen ->
.advance p
if .check (&TokenKind::RParen) p
.advance p
return Ok (Pattern::Tuple { items = Vec::new (), span = (span.0, (.prev_span p).1) })
inner = (pattern_alt p)?
if .check (&TokenKind::Comma) p
.advance p
mut items = vec! \-> inner
.extend (pattern_series p (&TokenKind::RParen))? items
(.expect (&TokenKind::RParen) "expected `)` to close the tuple pattern" p)?
return Ok (Pattern::Tuple { items, span = (span.0, (.prev_span p).1) })
(.expect (&TokenKind::RParen) "expected `)` to close a parenthesised pattern" p)?
Ok inner
TokenKind::Kw (Keyword::SelfValue) ->
.advance p
Ok $ Pattern::Binding $ Ident { text = .to_string "self", span }
TokenKind::Int _ or TokenKind::Float _ or TokenKind::Char _ or TokenKind::Byte _ or TokenKind::Kw (Keyword::True or Keyword::False) ->
text = token_text $ &(.peek p).kind
.advance p
Ok $ range_or_literal p text span
TokenKind::Str { raw, .. } ->
text = .clone raw
.advance p
Ok (Pattern::Literal { text, span })
_ ->
Err $ ParseError::r#with voltaic_diag::Code::ExpectedName "expected a parameter name, a literal pattern, a range, `_`, `(`, `[`, or `=`" span
export(crate) pattern_alt : &mut Parser -> Result<Pattern, ParseError>
pattern_alt p =
first = (pattern_app p)?
if !.check (&TokenKind::Kw Keyword::Or) p
return Ok first
mut alts = vec! \-> first
while .eat_keyword Keyword::Or p
.push (pattern_app p)? alts
Ok $ Pattern::Or alts
export(crate) pattern_app : &mut Parser -> Result<Pattern, ParseError>
pattern_app p =
start = (.peek_span p).0
guard constructor_path p is Some path else
return pattern p
if .check (&TokenKind::LBrace) p
return record_pattern p path start
mut args = Vec::new ()
while starts_a_pattern_argument p
.push (pattern p)? args
if .is_empty args
return Ok (Pattern::Binding (Ident { text = path, span = (start, (.prev_span p).1) }))
Ok (Pattern::Apply { path, args, span = (start, (.prev_span p).1) })
export(crate) pattern_series : &mut Parser -> &TokenKind -> Result<Vec<Pattern>, ParseError>
pattern_series p close =
mut items = Vec::new ()
.skip_layout p
while !.check close p
.push (pattern_alt p)? items
.skip_layout p
if !.check (&TokenKind::Comma) p
break
.advance p
.skip_layout p
Ok items
export(crate) patterns : &mut Parser -> Result<Vec<Pattern>, ParseError>
patterns p =
mut found = Vec::new ()
while !.at_eof p && !.check (&TokenKind::Eq) p && !.check (&TokenKind::Newline) p
.push (pattern p)? found
Ok found
export(crate) record_pattern : &mut Parser -> String -> usize -> Result<Pattern, ParseError>
record_pattern p head start =
mut path = head
while .check (&TokenKind::ColonColon) p
.advance p
segment = (.ident "expected a name after `::`" p)?
.push_str "::" path
.push_str (&segment.text) path
(.expect (&TokenKind::LBrace) "expected `{` to open a record pattern" p)?
mut fields = Vec::new ()
mut rest = false
.skip_layout p
while !.check (&TokenKind::RBrace) p
if .check (&TokenKind::DotDot) p
.advance p
rest := true
break
name = (.ident "expected a field name in a record pattern" p)?
bound = if .check (&TokenKind::Eq) p
.advance p
Some (pattern_alt p)?
else
None
.push (RecordPatternField { name, pattern = bound }) fields
.skip_layout p
if !.check (&TokenKind::Comma) p
break
.advance p
.skip_layout p
(.expect (&TokenKind::RBrace) "expected `}` to close the record pattern" p)?
Ok (Pattern::Record { path, fields, rest, span = (start, (.prev_span p).1) })
export(crate) starts_a_pattern_argument : &Parser -> bool
starts_a_pattern_argument p = case &(.peek p).kind of
TokenKind::Ident _ or TokenKind::Int _ or TokenKind::Float _ or TokenKind::Char _ or TokenKind::Byte _ or TokenKind::Str { .. } or TokenKind::LParen -> true
TokenKind::Kw (Keyword::True) or TokenKind::Kw (Keyword::False) or TokenKind::Kw (Keyword::SelfValue) -> true
_ -> false