Skip to content

Commit

Permalink
fix(parser): member and call expressions precedence.
Browse files Browse the repository at this point in the history
  • Loading branch information
rzvxa committed Mar 15, 2024
1 parent ca41a45 commit faad477
Show file tree
Hide file tree
Showing 15 changed files with 550 additions and 46 deletions.
126 changes: 125 additions & 1 deletion crates/fuse-ast/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ use fuse_common::{ReferenceType, Span};
use fuse_common_proc::serializable;
use std::{cell::Cell, rc::Rc};

use crate::GetSpan;

#[serializable]
#[derive(Debug)]
pub struct Chunk {
Expand Down Expand Up @@ -117,11 +119,35 @@ pub enum Expression {
ArrayExpression(Box<ArrayExpression>),
TupleExpression(Box<TupleExpression>),
ParenthesizedExpression(Box<ParenthesizedExpression>),
MemberExpression(Box<MemberExpression>),
CallExpression(Box<CallExpression>),
TableConstructionExpression(Box<ConstructionExpression>),
StructConstructionExpression(Box<StructConstructionExpression>),
}

impl Expression {
pub fn span(&self) -> Span {
use Expression::*;
match self {
NumberLiteral(expr) => expr.span,
StringLiteral(expr) => expr.span,
BooleanLiteral(expr) => expr.span,
Identifier(expr) => expr.span,
Function(expr) => expr.span,
If(expr) => expr.span,
UnaryOperator(expr) => expr.span(),
BinaryOperator(expr) => expr.span(),
ArrayExpression(expr) => expr.span,
TupleExpression(expr) => expr.span,
ParenthesizedExpression(expr) => expr.span,
MemberExpression(expr) => expr.span,
CallExpression(expr) => expr.span,
TableConstructionExpression(expr) => expr.span,
StructConstructionExpression(expr) => expr.span(),
}
}
}

#[serializable]
#[derive(Debug, PartialEq)]
pub struct BooleanLiteral {
Expand Down Expand Up @@ -264,12 +290,29 @@ pub struct UnaryOperator {
pub expression: Expression,
}

impl GetSpan for UnaryOperator {
#[inline]
fn span(&self) -> Span {
Span::with_spans(vec![self.kind.span(), self.expression.span()])
}
}

#[serializable]
#[derive(Debug, PartialEq)]
pub enum UnaryOperatorKind {
Not(Span),
Minus(Span),
Plus(Span),
Minus(Span),
}

impl GetSpan for UnaryOperatorKind {
fn span(&self) -> Span {
match self {
Self::Not(span) => *span,
Self::Plus(span) => *span,
Self::Minus(span) => *span,
}
}
}

#[serializable]
Expand All @@ -280,6 +323,12 @@ pub struct BinaryOperator {
pub rhs: Expression,
}

impl GetSpan for BinaryOperator {
fn span(&self) -> Span {
Span::with_spans(vec![self.kind.span(), self.lhs.span(), self.rhs.span()])
}
}

#[serializable]
#[derive(Debug, PartialEq)]
pub enum BinaryOperatorKind {
Expand Down Expand Up @@ -307,6 +356,36 @@ pub enum BinaryOperatorKind {
Member(Span),
}

impl GetSpan for BinaryOperatorKind {
fn span(&self) -> Span {
let span = match self {
Self::Assignment(span) => span,
Self::LogicalOr(span) => span,
Self::LogicalAnd(span) => span,
Self::BitwiseOr(span) => span,
Self::BitwiseXor(span) => span,
Self::BitwiseAnd(span) => span,
Self::Equality(span) => span,
Self::NonEquality(span) => span,
Self::LessThanEqual(span) => span,
Self::LessThan(span) => span,
Self::GreaterThanEqual(span) => span,
Self::GreaterThan(span) => span,
Self::Plus(span) => span,
Self::Minus(span) => span,
Self::Multiply(span) => span,
Self::Exponential(span) => span,
Self::Division(span) => span,
Self::FloorDivision(span) => span,
Self::Modulo(span) => span,
Self::ShiftLeft(span) => span,
Self::ShiftRight(span) => span,
Self::Member(span) => span,
};
*span
}
}

#[serializable]
#[derive(Debug, PartialEq)]
pub struct ArrayExpression {
Expand Down Expand Up @@ -403,13 +482,58 @@ pub struct CallExpression {
pub arguments: Vec<Expression>,
}

#[serializable]
#[derive(Debug, PartialEq)]
pub struct MemberExpression {
pub span: Span,
pub lhs: Box<MemberExpressionLHS>,
pub rhs: Box<MemberExpressionRHS>,
}

#[serializable]
#[derive(Debug, PartialEq)]
pub enum MemberExpressionLHS {
Identifier(Identifier),
Expression(Expression),
Member(MemberExpression),
Call(CallExpression),
}

impl From<MemberExpressionRHS> for MemberExpressionLHS {
fn from(value: MemberExpressionRHS) -> Self {
match value {
MemberExpressionRHS::Number(num) => {
Self::Expression(Expression::NumberLiteral(Box::from(num)))
}
MemberExpressionRHS::Identifier(ident) => Self::Identifier(ident),
MemberExpressionRHS::Member(member) => Self::Member(member),
MemberExpressionRHS::Call(call) => Self::Call(call),
}
}
}

#[serializable]
#[derive(Debug, PartialEq)]
pub enum MemberExpressionRHS {
Identifier(Identifier),
Number(NumberLiteral),
Member(MemberExpression),
Call(CallExpression),
}

#[serializable]
#[derive(Debug, PartialEq)]
pub struct StructConstructionExpression {
pub target: Expression,
pub construction: ConstructionExpression,
}

impl GetSpan for StructConstructionExpression {
fn span(&self) -> Span {
Span::with_spans(vec![self.target.span(), self.construction.span])
}
}

#[serializable]
#[derive(Debug, PartialEq)]
pub struct ConstructionExpression {
Expand Down
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 @@ -182,6 +182,19 @@ impl AstFactory {
}))
}

pub fn member_expression(
&self,
span: Span,
lhs: MemberExpressionLHS,
rhs: MemberExpressionRHS,
) -> Expression {
Expression::MemberExpression(Box::from(MemberExpression {
span,
lhs: Box::from(lhs),
rhs: Box::from(rhs),
}))
}

pub fn struct_construction_expression(
&self,
target: Expression,
Expand Down
5 changes: 5 additions & 0 deletions crates/fuse-ast/src/get_span.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
use fuse_common::Span;

pub trait GetSpan {
fn span(&self) -> Span;
}
2 changes: 2 additions & 0 deletions crates/fuse-ast/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
mod ast_factory;
mod get_span;
mod precedence;

pub mod ast;

pub use ast::*;
pub use ast_factory::*;
pub use get_span::*;
pub use precedence::*;
10 changes: 10 additions & 0 deletions crates/fuse-common/src/span.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::cmp;

use fuse_common_proc::serializable;

#[serializable]
Expand All @@ -12,4 +14,12 @@ impl Span {
pub const fn new(start: u32, end: u32) -> Self {
Self { start, end }
}

#[inline]
pub fn with_spans(spans: Vec<Self>) -> Self {
spans
.into_iter()
.reduce(|acc, e| Span::new(cmp::min(acc.start, e.start), cmp::max(acc.end, e.end)))
.expect("Attempt to construct a `Span` using `with_spans` with an empty list of spans.")
}
}
54 changes: 46 additions & 8 deletions crates/fuse-parser/src/parsers/expressions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use std::cell::Cell;
use crate::{lexer::TokenKind, Parser, ParserResult};
use fuse_ast::{
ArrayExpressionElement, BinaryOperator, BooleanLiteral, ConstructionExpression,
ConstructionField, Else, Expression, Identifier, If, KeyValueArgument, Precedence,
SpreadArgument, TupleExpressionElement,
ConstructionField, Else, Expression, Identifier, If, KeyValueArgument, MemberExpression,
MemberExpressionLHS, MemberExpressionRHS, Precedence, SpreadArgument, TupleExpressionElement,
};

impl<'a> Parser<'a> {
Expand All @@ -22,6 +22,11 @@ impl<'a> Parser<'a> {
}

pub(crate) fn try_parse_primary_expression(&mut self) -> Option<ParserResult<Expression>> {
let expr = self.try_parse_primary_expression_base()?;
Some(expr.and_then(|expr| self.parse_expression_with_suffix(expr)))
}

pub(crate) fn try_parse_primary_expression_base(&mut self) -> Option<ParserResult<Expression>> {
use TokenKind::*;
let expr = match self.cur_kind() {
True => {
Expand Down Expand Up @@ -59,11 +64,7 @@ impl<'a> Parser<'a> {
_ => return None,
};

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

Some(self.parse_expression_with_suffix(expr))
Some(expr)
}

pub(crate) fn parse_identifier(&mut self) -> ParserResult<Identifier> {
Expand Down Expand Up @@ -250,6 +251,7 @@ impl<'a> Parser<'a> {
match self.cur_kind() {
TokenKind::LCurly => self.parse_struct_construction_expression(expr),
TokenKind::LParen => self.parse_call_expression(expr),
TokenKind::Dot => self.parse_member_chain_expression_recursive(expr),
_ => Ok(expr),
}
}
Expand Down Expand Up @@ -320,7 +322,7 @@ impl<'a> Parser<'a> {
}

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

Expand All @@ -341,6 +343,42 @@ impl<'a> Parser<'a> {
.call_expression(self.end_span(start), expr, arguments))
}

fn parse_member_chain_expression_recursive(
&mut self,
expr: Expression,
) -> ParserResult<Expression> {
let start = expr.span();
if self.consume_if(TokenKind::Dot).is_none() {
return Ok(expr);
}

let lhs = {
match expr {
Expression::Identifier(lhs) => MemberExpressionLHS::Identifier(*lhs),
Expression::CallExpression(call) => MemberExpressionLHS::Call(*call),
Expression::MemberExpression(member) => MemberExpressionLHS::Member(*member),
_ => MemberExpressionLHS::Expression(expr),
}
};

// TODO: better error messages here, no panic, we should recover.
let rhs = match self.try_parse_primary_expression_base().expect("Expected a primary expression")? {
Expression::Identifier(ident) => MemberExpressionRHS::Identifier(*ident),
Expression::NumberLiteral(num) => MemberExpressionRHS::Number(*num),
Expression::CallExpression(call) => MemberExpressionRHS::Call(*call),
Expression::MemberExpression(member) => MemberExpressionRHS::Member(*member),
rhs => panic!("write error, member rhs should be identifier or number(only for tuple types). {rhs:?}"),
};

let expr = self.ast.member_expression(self.end_span(start), lhs, rhs);
let expr = match self.cur_kind() {
TokenKind::LParen => self.parse_call_expression(expr)?,
_ => expr,
};

self.parse_member_chain_expression_recursive(expr)
}

fn parse_expression_with_precedence(
&mut self,
expr: Expression,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Some(Chunk(
statements: [
Expression(CallExpression(CallExpression(
span: Span(
start: 4,
start: 0,
end: 6,
),
callee: Identifier(Identifier(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Some(Chunk(
statements: [
Expression(CallExpression(CallExpression(
span: Span(
start: 4,
start: 0,
end: 13,
),
callee: Identifier(Identifier(
Expand Down
Loading

0 comments on commit faad477

Please sign in to comment.