use super::*;
impl<'a> Parser<'a> {
pub(crate) fn parse_fn_decl(&mut self) -> Result<FnDecl, CompileError> {
let kw = self.expect(TokenKind::Fn, "to start a function declaration")?;
let first = self.expect_ident("after `fn`")?;
let name = if self.eat(TokenKind::Dot).is_some() {
let method = self.expect_ident("after `.` in a method declaration")?;
FnName::Method {
type_name: first,
method_name: method,
}
} else {
FnName::Free(first)
};
let mut type_params = Vec::new();
if self.peek_kind() == Some(TokenKind::LBracket) {
self.bump();
loop {
let p = self.expect_ident("as a type parameter name")?;
if self.peek_kind() == Some(TokenKind::Colon) {
let colon = self.bump().unwrap();
return Err(CompileError::new(
"bynk.generics.no_bounds",
colon.span,
format!(
"type parameter `{}` carries a bound — bounded generics are not in v0.20a",
p.name
),
)
.with_note("type parameters are unconstrained; remove the `: …` bound"));
}
type_params.push(TypeParam {
span: p.span,
name: p,
});
if self.eat(TokenKind::Comma).is_none() {
break;
}
}
self.expect(TokenKind::RBracket, "to close the type-parameter list")?;
}
self.expect(TokenKind::LParen, "after the function name")?;
let mut params = Vec::new();
let mut has_self = false;
if self.peek_kind() == Some(TokenKind::Self_) {
let self_tok = self.bump().unwrap();
if !matches!(name, FnName::Method { .. }) {
return Err(CompileError::new(
"bynk.parse.self_outside_method",
self_tok.span,
"`self` can only appear as the first parameter of a method declaration",
)
.with_note(
"use `fn TypeName.method(self, ...)` to declare a method, \
or remove `self` for a free function",
));
}
has_self = true;
if self.peek_kind() == Some(TokenKind::Comma) {
self.bump();
if self.peek_kind() != Some(TokenKind::RParen) {
params.push(self.parse_param()?);
while self.eat(TokenKind::Comma).is_some() {
params.push(self.parse_param()?);
}
}
}
} else if self.peek_kind() != Some(TokenKind::RParen) {
params.push(self.parse_param()?);
while self.eat(TokenKind::Comma).is_some() {
params.push(self.parse_param()?);
}
}
self.expect(TokenKind::RParen, "to close the parameter list")?;
self.expect(TokenKind::Arrow, "before the return type")?;
let return_type = self.parse_type_ref("as the return type")?;
let body = self.parse_block("to open the function body")?;
let span = kw.span.merge(body.span);
Ok(FnDecl {
type_params,
name,
params,
return_type,
body,
has_self,
documentation: None,
span,
trivia: Trivia::default(),
})
}
pub(crate) fn parse_block(&mut self, ctx: &str) -> Result<Block, CompileError> {
let open = self.expect(TokenKind::LBrace, ctx)?;
let mut statements = Vec::new();
let tail_leading: Vec<String>;
loop {
let leading = self.take_leading_trivia();
match self.peek_kind() {
Some(TokenKind::Let) | Some(TokenKind::Commit) | Some(TokenKind::Assert) => {
let mut stmt = self.parse_statement()?;
let trailing = self.take_trailing_trivia();
match &mut stmt {
Statement::Let(l) | Statement::EffectLet(l) => {
l.trivia.leading = leading;
l.trivia.trailing = trailing;
}
Statement::Commit(c) => {
c.trivia.leading = leading;
c.trivia.trailing = trailing;
}
Statement::Assert(a) => {
a.trivia.leading = leading;
a.trivia.trailing = trailing;
}
}
statements.push(stmt);
}
_ => {
tail_leading = leading;
break;
}
}
}
if self.peek_kind() == Some(TokenKind::RBrace)
&& matches!(statements.last(), Some(Statement::Assert(_)))
{
let close = self.expect(TokenKind::RBrace, "to close the block")?;
let tail = Expr {
kind: ExprKind::UnitLit,
span: close.span,
};
return Ok(Block {
statements,
tail: Box::new(tail),
span: open.span.merge(close.span),
tail_leading_comments: tail_leading,
});
}
let tail = self.parse_expr()?;
let close = self.expect(TokenKind::RBrace, "to close the block")?;
Ok(Block {
statements,
tail: Box::new(tail),
span: open.span.merge(close.span),
tail_leading_comments: tail_leading,
})
}
fn parse_statement(&mut self) -> Result<Statement, CompileError> {
if self.peek_kind() == Some(TokenKind::Commit) {
let kw = self.expect(TokenKind::Commit, "to start a commit statement")?;
let value = self.parse_expr()?;
let span = kw.span.merge(value.span);
return Ok(Statement::Commit(CommitStmt {
value,
span,
trivia: Trivia::default(),
}));
}
if self.peek_kind() == Some(TokenKind::Assert) {
let kw = self.expect(TokenKind::Assert, "to start an assert statement")?;
let value = self.parse_expr()?;
let span = kw.span.merge(value.span);
return Ok(Statement::Assert(AssertStmt {
value,
span,
trivia: Trivia::default(),
}));
}
let kw = self.expect(TokenKind::Let, "to start a let statement")?;
let name = if self.peek_kind() == Some(TokenKind::Underscore) {
let t = self.bump().unwrap();
Ident {
name: "_".to_string(),
span: t.span,
}
} else {
self.expect_ident("after `let`")?
};
let type_annot = if self.eat(TokenKind::Colon).is_some() {
Some(self.parse_type_ref("as the let-binding's type annotation")?)
} else {
None
};
match self.peek_kind() {
Some(TokenKind::Eq) => {
self.bump();
let value = self.parse_expr()?;
let span = kw.span.merge(value.span);
Ok(Statement::Let(LetStmt {
name,
type_annot,
value,
span,
trivia: Trivia::default(),
}))
}
Some(TokenKind::LArrow) => {
self.bump();
let value = self.parse_expr()?;
let span = kw.span.merge(value.span);
Ok(Statement::EffectLet(LetStmt {
name,
type_annot,
value,
span,
trivia: Trivia::default(),
}))
}
Some(_) => {
let t = self.peek().unwrap();
Err(CompileError::new(
"bynk.parse.expected_token",
t.span,
format!(
"expected `=` or `<-` after the let-binding's name, found {}",
t.kind.describe()
),
))
}
None => Err(CompileError::new(
"bynk.parse.unexpected_eof",
self.eof_span(),
"expected `=` or `<-` after the let-binding's name, found end of file",
)),
}
}
pub(crate) fn parse_param(&mut self) -> Result<Param, CompileError> {
let name = self.expect_ident("as a parameter name")?;
self.expect(TokenKind::Colon, "after the parameter name")?;
let type_ref = self.parse_type_ref("as the parameter type")?;
let span = name.span.merge(type_ref.span());
Ok(Param {
name,
type_ref,
span,
})
}
pub(crate) fn lambda_ahead(&self) -> bool {
let mut n = 1;
let mut depth = 1u32;
loop {
match self.tokens.get(self.pos + n).map(|t| t.kind) {
Some(TokenKind::LParen) => depth += 1,
Some(TokenKind::RParen) => {
depth -= 1;
if depth == 0 {
return self.tokens.get(self.pos + n + 1).map(|t| t.kind)
== Some(TokenKind::FatArrow);
}
}
None => return false,
_ => {}
}
n += 1;
}
}
pub(crate) fn parse_lambda(&mut self) -> Result<Expr, CompileError> {
let open = self.bump().unwrap(); let mut params = Vec::new();
if self.peek_kind() != Some(TokenKind::RParen) {
loop {
let name = self.expect_ident("as a lambda parameter name")?;
let mut p_span = name.span;
let type_ref = if self.eat(TokenKind::Colon).is_some() {
let t = self.parse_type_ref("as the lambda parameter type")?;
p_span = p_span.merge(t.span());
Some(t)
} else {
None
};
params.push(LambdaParam {
name,
type_ref,
span: p_span,
});
if self.eat(TokenKind::Comma).is_none() {
break;
}
}
}
self.expect(TokenKind::RParen, "to close the lambda parameter list")?;
self.expect(TokenKind::FatArrow, "after the lambda parameter list")?;
let body = if self.peek_kind() == Some(TokenKind::LBrace) {
let block = self.parse_block("as the lambda body")?;
let span = block.span;
Expr {
kind: ExprKind::Block(block),
span,
}
} else {
self.parse_expr()?
};
let span = open.span.merge(body.span);
Ok(Expr {
kind: ExprKind::Lambda(LambdaExpr {
params,
body: Box::new(body),
span,
}),
span,
})
}
}