import crate::ParseError
import crate::block::bind_block
import crate::block::block_expr
import crate::block::case_of
import crate::block::modified_block
import crate::operand::bracketed
import crate::operand::byte_literal
import crate::operand::interpolated_string
import crate::operand::lambda
import crate::operand::macro_call
import crate::operand::method_call_syntax
import crate::operand::optional_chaining
import crate::operand::record_literal
import crate::operand::section
import crate::parser::Parser
import crate::signature::TypeStop
import crate::signature::capture_type
import crate::support::PREFIX_POWER
import crate::support::binary_power
import crate::token::rust_hatch
import crate::token::rust_string_body
import voltaic_ast::BinaryOp
import voltaic_ast::Expr
import voltaic_ast::Fixity
import voltaic_ast::Ident
import voltaic_ast::TypeExpr
import voltaic_ast::UnaryOp
import voltaic_diag::Code
import voltaic_lexer::Keyword
import voltaic_lexer::StrPart
import voltaic_lexer::TokenKind
export(crate) const ABOVE_AND_AND : u8 = 10
export(crate) application : &mut Parser -> Result<Expr, ParseError>
application p =
mut head = (atom p)?
while !.just_dedented p && starts_argument (&(.peek p).kind)
mut args = Vec::new ()
while starts_argument (&(.peek p).kind)
.push (atom p)? args
span = ((.span head).0, (.prev_span p).1)
built = case head of
Expr::Field { recv, name, .. } -> Expr::Dot { recv, name, args, span }
other -> Expr::Call { callee = Box::new other, args, is_dollar = false, span }
head := built
loop
case (postfix p head)? of
Ok grown -> head := grown
Err unchanged ->
head := unchanged
break
Ok head
export(crate) atom : &mut Parser -> Result<Expr, ParseError>
atom p =
mut expr = (prefix p)?
loop
case (postfix p expr)? of
Ok grown -> expr := grown
Err unchanged ->
return Ok unchanged
export(crate) binary_op : &mut Parser -> Option<BinaryOp>
binary_op p =
found = (binary_power (&(.peek p).kind))?
.advance p
Some found.0
export(crate) digit_precedence : &str -> Option<u8>
digit_precedence text = case text of
"0" -> Some 0
"1" -> Some 1
"2" -> Some 2
"3" -> Some 3
"4" -> Some 4
"5" -> Some 5
"6" -> Some 6
"7" -> Some 7
"8" -> Some 8
"9" -> Some 9
_ -> None
export(crate) operator_chains : &Parser -> &Ident -> bool
operator_chains p spelling = case &(.peek p).kind of
TokenKind::UserOp next -> next == &spelling.text
_ -> false
export(crate) parse_expr : &mut Parser -> u8 -> Result<Expr, ParseError>
parse_expr p min =
mut lhs = (application p)?
loop
if min == 0 && .check (&TokenKind::Colon) p
.advance p
ty = (capture_type p "expected a type after `:`" TypeStop::Greedy)?
at = ((.span lhs).0, ty.span.1)
lhs := Expr::Ascribe { inner = Box::new lhs, ty, span = at }
continue
if min <= 1 && .check (&TokenKind::Dollar) p
.advance p
arg = (parse_expr p 1)?
at = ((.span lhs).0, (.span arg).1)
lhs := Expr::Call { callee = Box::new lhs, args = vec! \-> arg, is_dollar = true, span = at }
continue
if user_rung p is Some declared
if declared.2 < min
return Ok lhs
spelling = declared.0
.advance p
rhs = (parse_expr p declared.3)?
span = ((.span lhs).0, (.span rhs).1)
if declared.1 == Fixity::NonAssoc && operator_chains p (&spelling)
return Err $ ParseError::with_help Code::NonAssociativeOperator (format! \-> "`#{spelling.text}` was declared `infix`, so it does not chain") "write the grouping — `(a #{spelling.text} b) #{spelling.text} c` — or declare it `infixl` or `infixr`" (.peek_span p)
lhs := Expr::UserBinary { spelling, fixity = declared.1, precedence = declared.5, target = declared.4, lhs = Box::new lhs, rhs = Box::new rhs, span }
continue
guard binary_power (&(.peek p).kind) is Some rung else
if &(.peek p).kind is TokenKind::UserOp spelling
return Err $ ParseError::with_help Code::UndeclaredOperator (format! \-> "`#{spelling}` is not declared in this file") "declare it above its first use — `infixl 6 #{spelling} = Set::union`. An operator's scope is the file, which is plan 14 decision 4's refusal of name resolution" (.peek_span p)
return Ok lhs
if rung.1 < min
return Ok lhs
.advance p
rhs = (parse_expr p rung.2)?
span = ((.span lhs).0, (.span rhs).1)
lhs := Expr::Binary { op = rung.0, lhs = Box::new lhs, rhs = Box::new rhs, span }
export(crate) parse_spine : &mut Parser -> Result<Vec<TypeExpr>, ParseError>
parse_spine p =
mut spine = vec! \-> (capture_type p "expected a type after `::`" TypeStop::Greedy)?
while .check (&TokenKind::Arrow) p
.advance p
.push (capture_type p "expected a type after `->`" TypeStop::Greedy)? spine
Ok spine
#[allow(clippy::result_large_err)]
export(crate) postfix : &mut Parser -> Expr -> Result<Result<Expr, Expr>, ParseError>
postfix p lhs =
if .just_dedented p
return Ok (Err lhs)
if lhs is Expr::Unit { .. }
return Ok (Err lhs)
start = (.span lhs).0
if .check (&TokenKind::Question) p
glued = .is_some_and (\t -> t.kind == TokenKind::Dot && !t.ws_before) (.peek_at 1 p)
if glued
at = .peek_span p
return Err (optional_chaining (at.0, at.1 + 1))
.advance p
return Ok (Ok (Expr::Try { inner = Box::new lhs, span = (start, (.prev_span p).1) }))
if .check (&TokenKind::LBracket) p
.advance p
.skip_layout p
index = (parse_expr p 0)?
.skip_layout p
(.expect (&TokenKind::RBracket) "expected `]` to close the index" p)?
return Ok (Ok (Expr::Index { recv = Box::new lhs, index = Box::new index, span = (start, (.prev_span p).1) }))
if .check (&TokenKind::LBrace) p
return Ok (Ok (record_literal p lhs)?)
if .check (&TokenKind::Dot) p
.advance p
if &(.peek p).kind is TokenKind::Int digits
index_name = Ident { text = .clone digits, span = .peek_span p }
.advance p
return Ok (Ok (Expr::Field { recv = Box::new lhs, name = index_name, span = (start, (.prev_span p).1) }))
name = (.ident "expected a field name or a tuple index after `.`" p)?
if .check (&TokenKind::LParen) p && !(.peek p).ws_before
return Err (method_call_syntax (&name.text) (start, name.span.1))
return Ok (Ok (Expr::Field { recv = Box::new lhs, name, span = (start, (.prev_span p).1) }))
Ok $ Err lhs
export(crate) prefix : &mut Parser -> Result<Expr, ParseError>
prefix p =
start = (.peek_span p).0
if unary_op p is Some op
operand = (parse_expr p PREFIX_POWER)?
span = (start, (.span operand).1)
return Ok (Expr::Unary { op, operand = Box::new operand, span })
case .clone (.peek p).kind of
TokenKind::Int text or TokenKind::Float text ->
span = .peek_span p
.advance p
Ok (Expr::Num { text, span })
TokenKind::Str { parts, raw } ->
span = .peek_span p
.advance p
mut interpolates = false
for part in &parts
case part of
StrPart::Interp { .. } -> interpolates := true
_ -> ()
if interpolates
return interpolated_string (&parts) span
if .starts_with "\"\"\"" raw
mut joined = String::new ()
for part in &parts
case part of
StrPart::Text text -> .push_str (.as_str text) joined
StrPart::Interp { .. } ->
unreachable! \-> "checked above"
body = rust_string_body (&joined)
return Ok (Expr::StrLit { value = format! \-> "\"#{body}\"", span })
Ok (Expr::StrLit { value = raw, span })
TokenKind::RawStr { text, hashes } ->
span = .peek_span p
.advance p
pad = .repeat hashes "#"
Ok $ Expr::RawLit { text = format! \-> "r#{pad}\"#{text}\"#{pad}", span }
TokenKind::ByteStr text ->
span = .peek_span p
.advance p
body = rust_string_body (&text)
Ok $ Expr::RawLit { text = format! \-> "b\"#{body}\"", span }
TokenKind::CStr text ->
span = .peek_span p
.advance p
body = rust_string_body (&text)
Ok $ Expr::RawLit { text = format! \-> "c\"#{body}\"", span }
TokenKind::Byte value ->
span = .peek_span p
.advance p
Ok (Expr::RawLit { text = byte_literal value, span })
TokenKind::Kw (Keyword::Async) -> modified_block p Keyword::Async start
TokenKind::Kw (Keyword::Unsafe) -> modified_block p Keyword::Unsafe start
TokenKind::Kw (Keyword::Try) -> modified_block p Keyword::Try start
TokenKind::Char value ->
span = .peek_span p
.advance p
Ok (Expr::Char { value, span })
TokenKind::Kw (Keyword::True) ->
span = .peek_span p
.advance p
Ok (Expr::Bool { value = true, span })
TokenKind::Kw (Keyword::False) ->
span = .peek_span p
.advance p
Ok (Expr::Bool { value = false, span })
TokenKind::At ->
.advance p
name = (.ident "expected a field name after `@`" p)?
span = (start, name.span.1)
Ok (Expr::SelfField { name, span })
TokenKind::AtAt ->
.advance p
name = (.ident "expected a name after `@@`" p)?
span = (start, name.span.1)
Ok (Expr::SelfType { name, span })
TokenKind::LBracket -> bracketed p
TokenKind::LParen ->
.advance p
.skip_layout p
if .check (&TokenKind::RParen) p
.advance p
return Ok (Expr::Unit { span = (start, (.prev_span p).1) })
sectioned = (section p start)?
if sectioned is Some found
return Ok found
inner = (parse_expr p 0)?
.skip_layout p
if .check (&TokenKind::Comma) p
mut items = vec! \-> inner
while .check (&TokenKind::Comma) p
.advance p
.skip_layout p
if .check (&TokenKind::RParen) p
break
.push (parse_expr p 0)? items
.skip_layout p
(.expect (&TokenKind::RParen) "expected `)` to close the tuple" p)?
return Ok (Expr::Tuple { items, span = (start, (.prev_span p).1) })
(.expect (&TokenKind::RParen) "expected `)` to close the group" p)?
Ok (Expr::Paren { inner = Box::new inner, span = (start, (.prev_span p).1) })
TokenKind::Kw (Keyword::Rust) ->
.advance p
hatched = (rust_hatch p)?
Ok (Expr::RustHatch { code = hatched.0, span = (start, (.prev_span p).1) })
TokenKind::Backslash -> lambda p
TokenKind::Kw (Keyword::Move) -> lambda p
TokenKind::Kw (Keyword::If) -> .if_statement p
TokenKind::Kw (Keyword::Do) -> bind_block p start
TokenKind::Newline -> block_expr p None start
TokenKind::Label name ->
.advance p
block_expr p (Some name) start
TokenKind::Kw (Keyword::Case) -> case_of p
TokenKind::Kw (Keyword::SelfValue) ->
span = .peek_span p
.advance p
Ok $ Expr::Ident $ Ident { text = .to_string "self", span }
TokenKind::Kw (Keyword::SelfType) ->
span = .peek_span p
.advance p
Ok $ Expr::Ident $ Ident { text = .to_string "Self", span }
TokenKind::Dot ->
.advance p
name = (.ident "expected a method name after `.`" p)?
span = (start, name.span.1)
Ok (Expr::MethodRef { name, span })
TokenKind::Ident _ -> macro_call p
_ ->
Err $ ParseError::r#with Code::ExpectedExpression "expected an expression" (.peek_span p)
export(crate) starts_argument : &TokenKind -> bool
starts_argument kind = case kind of
TokenKind::Int _ or TokenKind::Float _ or TokenKind::Str { .. } or TokenKind::RawStr { .. } or TokenKind::ByteStr _ or TokenKind::CStr _ or TokenKind::Char _ or TokenKind::Byte _ or TokenKind::Ident _ or TokenKind::LParen or TokenKind::At or TokenKind::AtAt -> true
TokenKind::Kw (Keyword::True or Keyword::False or Keyword::SelfValue or Keyword::SelfType) -> true
_ -> false
export(crate) unary_op : &mut Parser -> Option<UnaryOp>
unary_op p =
op = case (.peek p).kind of
TokenKind::Minus -> UnaryOp::Neg
TokenKind::Bang -> UnaryOp::Not
TokenKind::Star -> UnaryOp::Deref
TokenKind::Amp ->
.advance p
if .eat_keyword Keyword::Mut p
return Some UnaryOp::RefMut
return Some UnaryOp::Ref
_ ->
return None
.advance p
Some op
export(crate) user_rung : &Parser -> Option<(Ident, Fixity, u8, u8, String, u8)>
user_rung p =
guard &(.peek p).kind is TokenKind::UserOp text else
return None
declared = (.get (.as_str text) p.fixities)?
fixity = declared.0
precedence = declared.1
powers = .powers precedence fixity
spelling = Ident { text = .clone text, span = .peek_span p }
Some (spelling, fixity, powers.0, powers.1, .clone declared.2, precedence)