Skip to content

Commit

Permalink
feat(parser): support for function declarations and expressions.
Browse files Browse the repository at this point in the history
  • Loading branch information
rzvxa committed Mar 4, 2024
1 parent 5f9ad0a commit 98342a7
Show file tree
Hide file tree
Showing 13 changed files with 149 additions and 99 deletions.
3 changes: 3 additions & 0 deletions crates/fuse-ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ pub enum Statement {
Expression(Box<Expression>),
/// A variable declaration using const, let or global keywords.
VariableDeclaration(Box<VariableDeclaration>),
/// A function declaration using function or fn keywords.
FunctionDeclaration(Box<Function>),
}

#[serializable]
Expand Down Expand Up @@ -170,6 +172,7 @@ pub struct Identifier {
#[derive(Debug, PartialEq)]
pub struct Function {
pub span: Span,
pub identifier: Option<Identifier>,
pub params: FunctionParameters,
pub return_type: Option<TypeAnnotation>,
pub body: FunctionBody,
Expand Down
10 changes: 9 additions & 1 deletion crates/fuse-ast/src/ast_factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,14 @@ impl AstFactory {
Statement::Empty(Box::from(EmptyStatement { span }))
}

pub fn declaration_statement(&self, decl: VariableDeclaration) -> Statement {
pub fn variable_declaration_statement(&self, decl: VariableDeclaration) -> Statement {
Statement::VariableDeclaration(Box::from(decl))
}

pub fn function_declaration_statement(&self, func: Function) -> Statement {
Statement::FunctionDeclaration(Box::from(func))
}

pub fn expression_statement(&self, expr: Expression) -> Statement {
Statement::Expression(Box::from(expr))
}
Expand All @@ -54,6 +58,10 @@ impl AstFactory {
}
}

pub fn function_declaration(&self) {

}

pub fn binding_identifier_pattern(
&self,
binding_identifier: BindingIdentifier,
Expand Down
4 changes: 4 additions & 0 deletions crates/fuse-parser/src/cursor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ impl<'a> Parser<'a> {
self.cur_token().kind
}

pub(crate) fn peek_token(&self) -> Option<&TokenReference> {
self.lexer.peek()
}

pub(crate) fn consume(&mut self) -> TokenReference {
let token = self.lexer.consume();
self.prev_token = token.clone();
Expand Down
7 changes: 5 additions & 2 deletions crates/fuse-parser/src/parsers/declarations.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
use crate::{lexer::TokenKind, Parser, ParserResult};
use fuse_ast::{VariableDeclaration, VariableDeclarationKind};
use fuse_common::Span;
use fuse_ast::{Function, VariableDeclaration, VariableDeclarationKind};

impl<'a> Parser<'a> {
pub(crate) fn parse_variable_declaration(&mut self) -> ParserResult<VariableDeclaration> {
Expand All @@ -26,4 +25,8 @@ impl<'a> Parser<'a> {
.ast
.variable_declaration(self.end_span(start), decl_kind, binding, expression))
}

pub(crate) fn parse_function_declaration(&mut self) -> ParserResult<Function> {
self.parse_function(true)
}
}
96 changes: 5 additions & 91 deletions crates/fuse-parser/src/parsers/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::{
Parser, ParserResult,
};
use fuse_ast::{
BindingPattern, BindingPatternKind, Block, BooleanLiteral, Expression, Function, FunctionBody,
BindingPattern, BindingPatternKind, BooleanLiteral, Expression, Function, FunctionBody,
FunctionParameter, FunctionParameters, Identifier, TypeAnnotation,
};
use fuse_common::Span;
Expand Down Expand Up @@ -36,9 +36,7 @@ impl<'a> Parser<'a> {
TokenKind::Identifier => self
.parse_identifier()
.map(|id| self.ast.identifier_expression(id)),
TokenKind::Function | TokenKind::Fn => self
.parse_function_expression()
.map(|func| self.ast.function_expression(func)),
TokenKind::Function | TokenKind::Fn => self.parse_function_expression(),
_ => Err(Self::unexpected_error(self.cur_token())),
}
}
Expand All @@ -52,92 +50,8 @@ impl<'a> Parser<'a> {
})
}

pub(crate) fn parse_function_expression(&mut self) -> ParserResult<Function> {
let start = self.start_span();
// Consume the keyword
self.consume();
let params = self.parse_function_parameters()?;
let return_type = self.parse_function_return_type()?;
let body = self.parse_function_body()?;
Ok(Function {
span: self.end_span(start),
params,
return_type,
body,
})
}

fn parse_function_parameters(&mut self) -> ParserResult<FunctionParameters> {
let open = self.consume_expect(TokenKind::LParen)?;
// Empty function parameters
if let Some(close) = self.consume_if(TokenKind::RParen) {
return Ok(FunctionParameters {
span: Span::new(open.start(), close.end()),
items: Vec::new(),
rest: None,
});
}

let mut params = Vec::new();
let mut seen_comma = true;

while self.at(TokenKind::Identifier) {
if !seen_comma {
return Err(Self::unexpect_token_kind_error(
self.cur_token(),
TokenKind::Comma,
));
}

let (param, comma) = self.parse_function_parameter()?;
seen_comma = comma.is_some();
params.push(param);
}

let close = self.consume_expect(TokenKind::RParen)?;
Ok(FunctionParameters {
span: Span::new(open.start(), close.end()),
items: params,
rest: None,
})
}

fn parse_function_parameter(
&mut self,
) -> ParserResult<(FunctionParameter, Option<TokenReference>)> {
let binding = self.parse_binding()?;
let BindingPattern {
kind: BindingPatternKind::Identifier(kind),
..
} = &binding
else {
todo!(
"Diagnosis error here,\
we don't allow variable deconstruction for function parameters."
);
};

Ok((
FunctionParameter {
span: kind.span,
pattern: binding,
},
self.consume_if(TokenKind::Comma),
))
}

fn parse_function_return_type(&mut self) -> ParserResult<Option<TypeAnnotation>> {
if self.consume_if(TokenKind::ThinArrow) == None {
return Ok(None);
}
self.parse_type_annotation().map(|t| Some(t))
}

fn parse_function_body(&mut self) -> ParserResult<FunctionBody> {
if let Some(_) = self.consume_if(TokenKind::Arrow) {
Ok(FunctionBody::Expression(self.parse_expression()?))
} else {
Ok(FunctionBody::Block(self.parse_block()?))
}
pub(crate) fn parse_function_expression(&mut self) -> ParserResult<Expression> {
self.parse_function(false)
.map(|func| self.ast.function_expression(func))
}
}
104 changes: 104 additions & 0 deletions crates/fuse-parser/src/parsers/functions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
use crate::{
lexer::{TokenKind, TokenReference},
Parser, ParserResult,
};
use fuse_ast::{
BindingPattern, BindingPatternKind, Function, FunctionBody, FunctionParameter,
FunctionParameters, TypeAnnotation,
};
use fuse_common::Span;
impl<'a> Parser<'a> {
pub(crate) fn parse_function(&mut self, expect_identifier: bool) -> ParserResult<Function> {
let start = self.start_span();
// Consume the keyword
self.consume();
let identifier = if expect_identifier {
Some(self.parse_identifier()?)
} else {
None
};
let params = self.parse_function_parameters()?;
let return_type = self.parse_function_return_type()?;
let body = self.parse_function_body()?;
Ok(Function {
span: self.end_span(start),
identifier,
params,
return_type,
body,
})
}
pub(crate) fn parse_function_parameters(&mut self) -> ParserResult<FunctionParameters> {
let open = self.consume_expect(TokenKind::LParen)?;
// Empty function parameters
if let Some(close) = self.consume_if(TokenKind::RParen) {
return Ok(FunctionParameters {
span: Span::new(open.start(), close.end()),
items: Vec::new(),
rest: None,
});
}

let mut params = Vec::new();
let mut seen_comma = true;

while self.at(TokenKind::Identifier) {
if !seen_comma {
return Err(Self::unexpect_token_kind_error(
self.cur_token(),
TokenKind::Comma,
));
}

let (param, comma) = self.parse_function_parameter()?;
seen_comma = comma.is_some();
params.push(param);
}

let close = self.consume_expect(TokenKind::RParen)?;
Ok(FunctionParameters {
span: Span::new(open.start(), close.end()),
items: params,
rest: None,
})
}

fn parse_function_parameter(
&mut self,
) -> ParserResult<(FunctionParameter, Option<TokenReference>)> {
let binding = self.parse_binding()?;
let BindingPattern {
kind: BindingPatternKind::Identifier(kind),
..
} = &binding
else {
todo!(
"Diagnosis error here,\
we don't allow variable deconstruction for function parameters."
);
};

Ok((
FunctionParameter {
span: kind.span,
pattern: binding,
},
self.consume_if(TokenKind::Comma),
))
}

pub(crate) fn parse_function_return_type(&mut self) -> ParserResult<Option<TypeAnnotation>> {
if self.consume_if(TokenKind::ThinArrow) == None {
return Ok(None);
}
self.parse_type_annotation().map(|t| Some(t))
}

pub(crate) fn parse_function_body(&mut self) -> ParserResult<FunctionBody> {
if let Some(_) = self.consume_if(TokenKind::Arrow) {
Ok(FunctionBody::Expression(self.parse_expression()?))
} else {
Ok(FunctionBody::Block(self.parse_block()?))
}
}
}
1 change: 1 addition & 0 deletions crates/fuse-parser/src/parsers/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod binding;
mod declarations;
mod expressions;
mod functions;
mod numbers;
mod statements;
mod strings;
Expand Down
18 changes: 13 additions & 5 deletions crates/fuse-parser/src/parsers/statements.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use fuse_ast::{Block, Statement};
use fuse_common::Span;

use crate::{lexer::TokenKind, Parser, ParserResult};

Expand Down Expand Up @@ -33,19 +32,28 @@ impl<'a> Parser<'a> {

TokenKind::Const | TokenKind::Let | TokenKind::Global => self
.parse_variable_declaration()
.map(|decl| self.ast.declaration_statement(decl)),
.map(|decl| self.ast.variable_declaration_statement(decl)),

TokenKind::True
| TokenKind::False
| TokenKind::NumberLiteral
| TokenKind::StringLiteral
| TokenKind::InterpolatedStringHead
| TokenKind::Identifier
| TokenKind::Function
| TokenKind::Fn => self
| TokenKind::Identifier => self
.parse_expression()
.map(|expr| self.ast.expression_statement(expr)),

TokenKind::Function | TokenKind::Fn => match self.peek_token() {
// function declaration
Some(peek) if peek.kind() == TokenKind::Identifier => self
.parse_function_declaration()
.map(|func| self.ast.function_declaration_statement(func)),
// function expression
_ => self
.parse_function_expression()
.map(|expr| self.ast.expression_statement(expr)),
},

kind if kind.is_trivial() => {
unreachable!("All trivial tokens should be eaten by a `TokenReference`.")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Some(Chunk(
start: 0,
end: 11,
),
identifier: None,
params: FunctionParameters(
span: Span(
start: 9,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Some(Chunk(
start: 0,
end: 19,
),
identifier: None,
params: FunctionParameters(
span: Span(
start: 2,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Some(Chunk(
start: 0,
end: 11,
),
identifier: None,
params: FunctionParameters(
span: Span(
start: 2,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Some(Chunk(
start: 0,
end: 13,
),
identifier: None,
params: FunctionParameters(
span: Span(
start: 2,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ Some(Chunk(
start: 0,
end: 19,
),
identifier: None,
params: FunctionParameters(
span: Span(
start: 2,
Expand Down

0 comments on commit 98342a7

Please sign in to comment.