Skip to content

Commit

Permalink
improvement(parser): better function parsing.
Browse files Browse the repository at this point in the history
  • Loading branch information
rzvxa committed Mar 4, 2024
1 parent 57f0d07 commit d442279
Show file tree
Hide file tree
Showing 14 changed files with 409 additions and 35 deletions.
2 changes: 1 addition & 1 deletion crates/fuse-ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ pub struct Function {
pub span: Span,
pub params: FunctionParameters,
pub return_type: Option<TypeAnnotation>,
pub body: Option<FunctionBody>,
pub body: FunctionBody,
}

#[serializable]
Expand Down
13 changes: 13 additions & 0 deletions crates/fuse-parser/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,21 @@ pub enum Error {
token: TokenReference,
expected: TokenKind,
},
#[error("{0}")]
DiagnosisError(DiagnosisError),
#[error("Invalid number literal error at {0:?}")]
InvalidNumberLiteralError(TokenReference),
#[error("Unexpected error at {0:?}")]
UnexpectedError(TokenReference),
}

#[serializable]
#[derive(ThisError, Debug)]
pub enum DiagnosisError {
#[error("{0:?}\n{1}")]
GeneralError(TokenReference, String),
}

impl<'a> Parser<'a> {
pub(crate) fn unexpected_error(token: &TokenReference) -> Error {
Error::UnexpectedError(token.clone())
Expand All @@ -36,4 +45,8 @@ impl<'a> Parser<'a> {
pub(crate) fn invalid_number_literal_error(token: &TokenReference) -> Error {
Error::UnexpectedError(token.clone())
}

pub(crate) fn diagnosis_general_error(token: &TokenReference, msg: &str) -> Error {
Error::DiagnosisError(DiagnosisError::GeneralError(token.clone(), msg.to_string()))
}
}
3 changes: 3 additions & 0 deletions crates/fuse-parser/src/lexer/operator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ impl<'a> Lexer<'a> {
pub(super) fn operator(&mut self, start: u32, first: char) -> Option<Token> {
let source = &mut self.source;
let kind = flash_match! ((source, start, first) {
',' => {
"" => TokenKind::Comma,
}
'.' => {
"" => TokenKind::Dot,
"." => TokenKind::Dot2,
Expand Down
73 changes: 44 additions & 29 deletions crates/fuse-parser/src/parsers/expressions.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
use crate::{lexer::TokenKind, Parser, ParserResult};
use crate::{
lexer::{TokenKind, TokenReference},
Parser, ParserResult,
};
use fuse_ast::{
BindingPattern, BindingPatternKind, Block, BooleanLiteral, Expression, Function,
BindingPattern, BindingPatternKind, Block, BooleanLiteral, Expression, Function, FunctionBody,
FunctionParameter, FunctionParameters, Identifier, TypeAnnotation,
};
use fuse_common::Span;
Expand Down Expand Up @@ -55,12 +58,12 @@ impl<'a> Parser<'a> {
self.consume();
let params = self.parse_function_parameters()?;
let return_type = self.parse_function_return_type()?;
let end = self.consume_expect(TokenKind::End);
let body = self.parse_function_body()?;
Ok(Function {
span: self.end_span(start),
params,
return_type,
body: None,
body,
})
}

Expand All @@ -76,31 +79,20 @@ impl<'a> Parser<'a> {
}

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

while self.at(TokenKind::Identifier) {
if first_param {
first_param = false;
} else {
self.consume_expect(TokenKind::Comma)?;
if !seen_comma {
return Err(Self::unexpect_token_kind_error(
self.cur_token(),
TokenKind::Comma,
));
}

let binding = self.parse_binding()?;
match &binding {
BindingPattern {
kind: BindingPatternKind::Identifier(kind),
..
} => params.push(FunctionParameter {
span: kind.span,
pattern: binding,
}),
_ => todo!(
"Diagnosis error here,\
we don't allow variable deconstruction for function parameters."
),
}
let (param, comma) = self.parse_function_parameter()?;
seen_comma = comma.is_some();
params.push(param);
}
// accept trailing commas.
self.consume_if(TokenKind::Comma);

let close = self.consume_expect(TokenKind::RParen)?;
Ok(FunctionParameters {
Expand All @@ -110,19 +102,42 @@ impl<'a> Parser<'a> {
})
}

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<Block> {
fn parse_function_body(&mut self) -> ParserResult<FunctionBody> {
if let Some(_) = self.consume_if(TokenKind::Arrow) {
self.parse_expression();
todo!()
Ok(FunctionBody::Expression(self.parse_expression()?))
} else {
self.parse_block()
Ok(FunctionBody::Block(self.parse_block()?))
}
}
}
2 changes: 1 addition & 1 deletion crates/fuse-parser/src/parsers/statements.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ impl<'a> Parser<'a> {
pub(crate) fn parse_statements(&mut self) -> ParserResult<Vec<Statement>> {
let mut statements = Vec::new();

while !self.at(TokenKind::Eof) {
while !self.at(TokenKind::Eof) && !self.at(TokenKind::End) {
match self.parse_statement() {
ParserResult::Ok(stmt) => {
statements.push(stmt);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Some(Chunk(
Expression(Function(Function(
span: Span(
start: 0,
end: 15,
end: 11,
),
params: FunctionParameters(
span: Span(
Expand All @@ -24,7 +24,9 @@ Some(Chunk(
rest: None,
),
return_type: None,
body: None,
body: Block(Block(
statements: [],
)),
))),
],
),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
source: crates/fuse-parser/tests/cases/mod.rs
expression: parsed.chunk
input_file: crates/fuse-parser/tests/cases/pass/function-expression-02/case.fuse
---
Some(Chunk(
span: Span(
start: 0,
end: 24,
),
body: Block(
statements: [
Expression(Function(Function(
span: Span(
start: 0,
end: 19,
),
params: FunctionParameters(
span: Span(
start: 2,
end: 4,
),
items: [],
rest: None,
),
return_type: None,
body: Block(Block(
statements: [
Expression(StringLiteral(StringLiteral(
span: Span(
start: 6,
end: 19,
),
segments: [
Literal(Unescaped(Span(
start: 7,
end: 19,
))),
],
))),
],
)),
))),
],
),
))
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
fn()
"Hello world"
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
---
source: crates/fuse-parser/tests/cases/mod.rs
expression: tokens
input_file: crates/fuse-parser/tests/cases/pass/function-expression-02/case.fuse
---
[
TokenReference(
token: Token(
span: Span(
start: 0,
end: 2,
),
kind: Fn,
),
leading_trivia: [],
trailing_trivia: [],
),
TokenReference(
token: Token(
span: Span(
start: 2,
end: 3,
),
kind: LParen,
),
leading_trivia: [],
trailing_trivia: [],
),
TokenReference(
token: Token(
span: Span(
start: 3,
end: 4,
),
kind: RParen,
),
leading_trivia: [],
trailing_trivia: [
Token(
span: Span(
start: 4,
end: 6,
),
kind: Whitespace,
),
],
),
TokenReference(
token: Token(
span: Span(
start: 6,
end: 19,
),
kind: StringLiteral,
),
leading_trivia: [],
trailing_trivia: [
Token(
span: Span(
start: 19,
end: 20,
),
kind: Whitespace,
),
],
),
TokenReference(
token: Token(
span: Span(
start: 20,
end: 23,
),
kind: End,
),
leading_trivia: [],
trailing_trivia: [
Token(
span: Span(
start: 23,
end: 24,
),
kind: Whitespace,
),
],
),
]
Loading

0 comments on commit d442279

Please sign in to comment.