Skip to content

Commit

Permalink
feat(parser): add call expression and dot operator parsers.
Browse files Browse the repository at this point in the history
  • Loading branch information
rzvxa committed Mar 9, 2024
1 parent 4ae8a08 commit 0c25547
Show file tree
Hide file tree
Showing 15 changed files with 520 additions and 34 deletions.
10 changes: 10 additions & 0 deletions crates/fuse-ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ pub enum Expression {
ArrayExpression(Box<ArrayExpression>),
TupleExpression(Box<TupleExpression>),
ParenthesizedExpression(Box<ParenthesizedExpression>),
CallExpression(Box<CallExpression>),
}

#[serializable]
Expand Down Expand Up @@ -282,6 +283,7 @@ pub enum BinaryOperatorKind {
Modulo(Span),
ShiftLeft(Span),
ShiftRight(Span),
Member(Span),
}

#[serializable]
Expand Down Expand Up @@ -363,3 +365,11 @@ pub struct ParenthesizedExpression {
pub span: Span,
pub expression: Expression,
}

#[serializable]
#[derive(Debug, PartialEq)]
pub struct CallExpression {
pub span: Span,
pub target: Expression,
pub arguments: Vec<Expression>,
}
13 changes: 13 additions & 0 deletions crates/fuse-ast/src/ast_factory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,4 +159,17 @@ impl AstFactory {
pub fn parenthesized_expression(&self, span: Span, expression: Expression) -> Expression {
Expression::ParenthesizedExpression(Box::from(ParenthesizedExpression { span, expression }))
}

pub fn call_expression(
&self,
span: Span,
target: Expression,
arguments: Vec<Expression>,
) -> Expression {
Expression::CallExpression(Box::from(CallExpression {
span,
target,
arguments,
}))
}
}
4 changes: 2 additions & 2 deletions crates/fuse-ast/src/precedence.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ pub enum Precedence {
/// used as 0 value of enum.
Expression,
Assignment,
Construction,
LogicalOr,
LogicalAnd,
BitwiseOr,
Expand All @@ -19,11 +18,12 @@ pub enum Precedence {
Add,
Multiply,
Exponential,
Member,
}

impl Precedence {
pub fn is_right_associative(&self) -> bool {
matches!(self, Self::Assignment | Self::Construction)
matches!(self, Self::Assignment | Self::Member)
}

pub fn is_left_associative(&self) -> bool {
Expand Down
2 changes: 1 addition & 1 deletion crates/fuse-parser/src/lexer/token_kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,6 @@ impl TokenKind {
use TokenKind::*;
match self {
Eq => Some(Assignment),
LCurly => Some(Construction),
Or => Some(LogicalOr),
And => Some(LogicalAnd),
Pipe => Some(BitwiseOr),
Expand All @@ -223,6 +222,7 @@ impl TokenKind {
Plus | Minus => Some(Add),
Star | Slash | Slash2 | Percent => Some(Multiply),
Star2 => Some(Exponential),
Dot => Some(Member),
_ => None,
}
}
Expand Down
92 changes: 61 additions & 31 deletions crates/fuse-parser/src/parsers/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use fuse_ast::{
impl<'a> Parser<'a> {
pub(crate) fn parse_expression(&mut self) -> ParserResult<Expression> {
let expr = self.parse_primary_expression()?;
let expr = self.parse_expression_with_suffix(expr)?;
self.parse_expression_with_precedence(expr, Precedence::Expression)
}

Expand All @@ -21,43 +20,46 @@ impl<'a> Parser<'a> {

pub(crate) fn try_parse_primary_expression(&mut self) -> Option<ParserResult<Expression>> {
use TokenKind::*;
match self.cur_kind() {
let expr = match self.cur_kind() {
True => {
let token = self.consume();
Some(Ok(self.ast.boolean_expression(BooleanLiteral {
Ok(self.ast.boolean_expression(BooleanLiteral {
span: token.span(),
value: true,
})))
}))
}
False => {
let token = self.consume();
Some(Ok(self.ast.boolean_expression(BooleanLiteral {
Ok(self.ast.boolean_expression(BooleanLiteral {
span: token.span(),
value: false,
})))
}))
}
NumberLiteral => Some(
self.parse_number_literal()
.map(|expr| self.ast.number_expression(expr)),
),
StringLiteral | InterpolatedStringHead => Some(
self.parse_string_literal()
.map(|expr| self.ast.string_expression(expr)),
),
Identifier => Some(
self.parse_identifier()
.map(|id| self.ast.identifier_expression(id)),
),

Function | TokenKind::Fn => Some(self.parse_function_expression()),
If => Some(self.parse_if_expression()),

Not | Plus | Minus => Some(self.parse_unary_operator_expression()),
LBrack => Some(self.parse_array_expression()),
LParen => Some(self.parse_tuple_or_parenthesized_expression()),

_ => None,
}
NumberLiteral => self
.parse_number_literal()
.map(|expr| self.ast.number_expression(expr)),
StringLiteral | InterpolatedStringHead => self
.parse_string_literal()
.map(|expr| self.ast.string_expression(expr)),
Identifier => self
.parse_identifier()
.map(|id| self.ast.identifier_expression(id)),

Function | TokenKind::Fn => self.parse_function_expression(),
If => self.parse_if_expression(),

Not | Plus | Minus => self.parse_unary_operator_expression(),
LBrack => self.parse_array_expression(),
LParen => self.parse_tuple_or_parenthesized_expression(),

_ => return None,
};

let Ok(expr) = expr else {
return Some(expr);
};

Some(self.parse_expression_with_suffix(expr))
}

pub(crate) fn parse_identifier(&mut self) -> ParserResult<Identifier> {
Expand Down Expand Up @@ -224,13 +226,41 @@ impl<'a> Parser<'a> {
TokenKind::LCurly => {
todo!("parse construction")
}
TokenKind::LParen => {
todo!("parse call expression")
}
TokenKind::LParen => self.parse_call_expression(expr),
_ => Ok(expr),
}
}

fn parse_call_expression(&mut self, lhs: Expression) -> ParserResult<Expression> {
let start = self.start_span();
// consume the open parentheses
self.consume();
let mut arguments: Vec<Expression> = Vec::new();

// return early for calls with no arguments.
if self.consume_if(TokenKind::RParen).is_some() {
return Ok(self
.ast
.call_expression(self.end_span(start), lhs, arguments));
}

loop {
let argument = self.parse_expression()?;

arguments.push(argument);

if self.consume_if(TokenKind::Comma).is_none() {
break;
}
}

self.consume_expect(TokenKind::RParen)?;

Ok(self
.ast
.call_expression(self.end_span(start), lhs, arguments))
}

fn parse_expression_with_precedence(
&mut self,
lhs: Expression,
Expand Down
1 change: 1 addition & 0 deletions crates/fuse-parser/src/parsers/operators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ impl<'a> Parser<'a> {
Percent => Modulo
LShift => ShiftLeft
RShift => ShiftRight
Dot => Member
}
}

Expand Down
30 changes: 30 additions & 0 deletions crates/fuse-parser/tests/cases/pass/call-expression-01/ast.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
---
source: crates/fuse-parser/tests/cases/mod.rs
description: "test()\n"
expression: parsed.chunk
input_file: crates/fuse-parser/tests/cases/pass/call-expression-01/case.fuse
---
Some(Chunk(
span: Span(
start: 0,
end: 7,
),
body: Block(
statements: [
Expression(CallExpression(CallExpression(
span: Span(
start: 4,
end: 6,
),
target: Identifier(Identifier(
span: Span(
start: 0,
end: 4,
),
name: Atom("test"),
)),
arguments: [],
))),
],
),
))
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test()
49 changes: 49 additions & 0 deletions crates/fuse-parser/tests/cases/pass/call-expression-01/tokens.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
---
source: crates/fuse-parser/tests/cases/mod.rs
description: "test()\n"
expression: tokens
input_file: crates/fuse-parser/tests/cases/pass/call-expression-01/case.fuse
---
[
TokenReference(
token: Token(
span: Span(
start: 0,
end: 4,
),
kind: Identifier,
),
leading_trivia: [],
trailing_trivia: [],
),
TokenReference(
token: Token(
span: Span(
start: 4,
end: 5,
),
kind: LParen,
),
leading_trivia: [],
trailing_trivia: [],
),
TokenReference(
token: Token(
span: Span(
start: 5,
end: 6,
),
kind: RParen,
),
leading_trivia: [],
trailing_trivia: [
Token(
span: Span(
start: 6,
end: 7,
),
kind: Whitespace,
),
],
),
]
52 changes: 52 additions & 0 deletions crates/fuse-parser/tests/cases/pass/call-expression-02/ast.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
source: crates/fuse-parser/tests/cases/mod.rs
description: "test(a, b, c)\n"
expression: parsed.chunk
input_file: crates/fuse-parser/tests/cases/pass/call-expression-02/case.fuse
---
Some(Chunk(
span: Span(
start: 0,
end: 14,
),
body: Block(
statements: [
Expression(CallExpression(CallExpression(
span: Span(
start: 4,
end: 13,
),
target: Identifier(Identifier(
span: Span(
start: 0,
end: 4,
),
name: Atom("test"),
)),
arguments: [
Identifier(Identifier(
span: Span(
start: 5,
end: 6,
),
name: Atom("a"),
)),
Identifier(Identifier(
span: Span(
start: 8,
end: 9,
),
name: Atom("b"),
)),
Identifier(Identifier(
span: Span(
start: 11,
end: 12,
),
name: Atom("c"),
)),
],
))),
],
),
))
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
test(a, b, c)
Loading

0 comments on commit 0c25547

Please sign in to comment.